/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- *//* vim:set expandtab ts=4 sw=4 sts=4 cin: *//* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */// HttpLog.h should generally be included first#include"HttpLog.h"#include<inttypes.h>#include"mozilla/dom/nsCSPContext.h"#include"mozilla/ScopeExit.h"#include"mozilla/SizePrintfMacros.h"#include"mozilla/Sprintf.h"#include"nsHttp.h"#include"nsHttpChannel.h"#include"nsHttpHandler.h"#include"nsIApplicationCacheService.h"#include"nsIApplicationCacheContainer.h"#include"nsICacheStorageService.h"#include"nsICacheStorage.h"#include"nsICacheEntry.h"#include"nsICaptivePortalService.h"#include"nsICryptoHash.h"#include"nsINetworkInterceptController.h"#include"nsINSSErrorsService.h"#include"nsISecurityReporter.h"#include"nsIStringBundle.h"#include"nsIStreamListenerTee.h"#include"nsISeekableStream.h"#include"nsILoadGroupChild.h"#include"nsIProtocolProxyService2.h"#include"nsIURIClassifier.h"#include"nsMimeTypes.h"#include"nsNetCID.h"#include"nsNetUtil.h"#include"nsIURL.h"#include"nsIStreamTransportService.h"#include"prnetdb.h"#include"nsEscape.h"#include"nsStreamUtils.h"#include"nsIOService.h"#include"nsDNSPrefetch.h"#include"nsChannelClassifier.h"#include"nsIRedirectResultListener.h"#include"mozilla/dom/ContentVerifier.h"#include"mozilla/TimeStamp.h"#include"nsError.h"#include"nsPrintfCString.h"#include"nsAlgorithm.h"#include"nsQueryObject.h"#include"nsThreadUtils.h"#include"GeckoProfiler.h"#include"nsIConsoleService.h"#include"mozilla/Attributes.h"#include"mozilla/DebugOnly.h"#include"mozilla/Preferences.h"#include"nsISSLSocketControl.h"#include"sslt.h"#include"nsContentUtils.h"#include"nsContentSecurityManager.h"#include"nsIClassOfService.h"#include"nsIPermissionManager.h"#include"nsIPrincipal.h"#include"nsIScriptError.h"#include"nsIScriptSecurityManager.h"#include"nsISSLStatus.h"#include"nsISSLStatusProvider.h"#include"nsITransportSecurityInfo.h"#include"nsIWebProgressListener.h"#include"LoadContextInfo.h"#include"netCore.h"#include"nsHttpTransaction.h"#include"nsICacheEntryDescriptor.h"#include"nsICancelable.h"#include"nsIHttpChannelAuthProvider.h"#include"nsIHttpChannelInternal.h"#include"nsIHttpEventSink.h"#include"nsIPrompt.h"#include"nsInputStreamPump.h"#include"nsURLHelper.h"#include"nsISocketTransport.h"#include"nsIStreamConverterService.h"#include"nsISiteSecurityService.h"#include"nsString.h"#include"nsCRT.h"#include"CacheObserver.h"#include"mozilla/dom/Performance.h"#include"mozilla/Telemetry.h"#include"AlternateServices.h"#include"InterceptedChannel.h"#include"nsIHttpPushListener.h"#include"nsIX509Cert.h"#include"ScopedNSSTypes.h"#include"NullPrincipal.h"#include"nsIDeprecationWarner.h"#include"nsIDocument.h"#include"nsIDOMDocument.h"#include"nsICompressConvStats.h"#include"nsCORSListenerProxy.h"#include"nsISocketProvider.h"#include"mozilla/net/Predictor.h"#include"mozilla/MathAlgorithms.h"#include"CacheControlParser.h"#include"nsMixedContentBlocker.h"#include"HSTSPrimerListener.h"#include"CacheStorageService.h"#include"HttpChannelParent.h"#include"nsIBufferedStreams.h"#include"nsIFileStreams.h"#include"nsIMIMEInputStream.h"#include"nsIMultiplexInputStream.h"#include"../../cache2/CacheFileUtils.h"#ifdef MOZ_TASK_TRACER#include"GeckoTaskTracer.h"#endifnamespacemozilla{namespacenet{namespace{// Monotonically increasing ID for generating unique cache entries per// intercepted channel.staticuint64_tgNumIntercepted=0;staticboolsRCWNEnabled=false;staticuint32_tsRCWNQueueSizeNormal=50;staticuint32_tsRCWNQueueSizePriority=10;staticuint32_tsRCWNSmallResourceSizeKB=256;// True if the local cache should be bypassed when processing a request.#define BYPASS_LOCAL_CACHE(loadFlags) \ (loadFlags & (nsIRequest::LOAD_BYPASS_CACHE | \ nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE))#define RECOVER_FROM_CACHE_FILE_ERROR(result) \ ((result) == NS_ERROR_FILE_NOT_FOUND || \ (result) == NS_ERROR_FILE_CORRUPTED || \ (result) == NS_ERROR_OUT_OF_MEMORY)#define WRONG_RACING_RESPONSE_SOURCE(req) \ (mRaceCacheWithNetwork && \ (((mFirstResponseSource == RESPONSE_FROM_CACHE) && (req != mCachePump)) || \ ((mFirstResponseSource == RESPONSE_FROM_NETWORK) && (req != mTransactionPump))))staticNS_DEFINE_CID(kStreamListenerTeeCID,NS_STREAMLISTENERTEE_CID);enumCacheDisposition{kCacheHit=1,kCacheHitViaReval=2,kCacheMissedViaReval=3,kCacheMissed=4};voidAccumulateCacheHitTelemetry(CacheDispositionhitOrMiss){if(!CacheObserver::UseNewCache()){Telemetry::Accumulate(Telemetry::HTTP_CACHE_DISPOSITION_2,hitOrMiss);}else{Telemetry::Accumulate(Telemetry::HTTP_CACHE_DISPOSITION_2_V2,hitOrMiss);int32_texperiment=CacheObserver::HalfLifeExperiment();if(experiment>0&&hitOrMiss==kCacheMissed){Telemetry::Accumulate(Telemetry::HTTP_CACHE_MISS_HALFLIFE_EXPERIMENT_2,experiment-1);}}}// Computes and returns a SHA1 hash of the input buffer. The input buffer// must be a null-terminated string.nsresultHash(constchar*buf,nsACString&hash){nsresultrv;nsCOMPtr<nsICryptoHash>hasher=do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID,&rv);NS_ENSURE_SUCCESS(rv,rv);rv=hasher->Init(nsICryptoHash::SHA1);NS_ENSURE_SUCCESS(rv,rv);rv=hasher->Update(reinterpret_cast<unsignedconstchar*>(buf),strlen(buf));NS_ENSURE_SUCCESS(rv,rv);rv=hasher->Finish(true,hash);NS_ENSURE_SUCCESS(rv,rv);returnNS_OK;}}// unnamed namespace// We only treat 3xx responses as redirects if they have a Location header and// the status code is in a whitelist.boolnsHttpChannel::WillRedirect(nsHttpResponseHead*response){returnIsRedirectStatus(response->Status())&&response->HasHeader(nsHttp::Location);}nsresultStoreAuthorizationMetaData(nsICacheEntry*entry,nsHttpRequestHead*requestHead);classAutoRedirectVetoNotifier{public:explicitAutoRedirectVetoNotifier(nsHttpChannel*channel):mChannel(channel){if(mChannel->mHasAutoRedirectVetoNotifier){MOZ_CRASH("Nested AutoRedirectVetoNotifier on the stack");mChannel=nullptr;return;}mChannel->mHasAutoRedirectVetoNotifier=true;}~AutoRedirectVetoNotifier(){ReportRedirectResult(false);}voidRedirectSucceeded(){ReportRedirectResult(true);}private:nsHttpChannel*mChannel;voidReportRedirectResult(boolsucceeded);};voidAutoRedirectVetoNotifier::ReportRedirectResult(boolsucceeded){if(!mChannel)return;mChannel->mRedirectChannel=nullptr;nsCOMPtr<nsIRedirectResultListener>vetoHook;NS_QueryNotificationCallbacks(mChannel,NS_GET_IID(nsIRedirectResultListener),getter_AddRefs(vetoHook));nsHttpChannel*channel=mChannel;mChannel=nullptr;if(vetoHook)vetoHook->OnRedirectResult(succeeded);// Drop after the notificationchannel->mHasAutoRedirectVetoNotifier=false;}//-----------------------------------------------------------------------------// nsHttpChannel <public>//-----------------------------------------------------------------------------nsHttpChannel::nsHttpChannel():HttpAsyncAborter<nsHttpChannel>(this),mLogicalOffset(0),mPostID(0),mRequestTime(0),mOfflineCacheLastModifiedTime(0),mSuspendTotalTime(0),mInterceptCache(DO_NOT_INTERCEPT),mInterceptionID(gNumIntercepted++),mCacheOpenWithPriority(false),mCacheQueueSizeWhenOpen(0),mCachedContentIsValid(false),mCachedContentIsPartial(false),mCacheOnlyMetadata(false),mTransactionReplaced(false),mAuthRetryPending(false),mProxyAuthPending(false),mCustomAuthHeader(false),mResuming(false),mInitedCacheEntry(false),mFallbackChannel(false),mCustomConditionalRequest(false),mFallingBack(false),mWaitingForRedirectCallback(false),mRequestTimeInitialized(false),mCacheEntryIsReadOnly(false),mCacheEntryIsWriteOnly(false),mCacheEntriesToWaitFor(0),mHasQueryString(0),mConcurrentCacheAccess(0),mIsPartialRequest(0),mHasAutoRedirectVetoNotifier(0),mPinCacheContent(0),mIsCorsPreflightDone(0),mStronglyFramed(false),mUsedNetwork(0),mAuthConnectionRestartable(0),mReqContentLengthDetermined(0),mReqContentLength(0U),mPushedStream(nullptr),mLocalBlocklist(false),mWarningReporter(nullptr),mIsReadingFromCache(false),mOnCacheAvailableCalled(false),mRaceCacheWithNetwork(false),mCacheAsyncOpenCalled(false),mDidReval(false){LOG(("Creating nsHttpChannel [this=%p]\n",this));mChannelCreationTime=PR_Now();mChannelCreationTimestamp=TimeStamp::Now();}nsHttpChannel::~nsHttpChannel(){LOG(("Destroying nsHttpChannel [this=%p]\n",this));if(mAuthProvider){DebugOnly<nsresult>rv=mAuthProvider->Disconnect(NS_ERROR_ABORT);MOZ_ASSERT(NS_SUCCEEDED(rv));}ReleaseMainThreadOnlyReferences();}voidnsHttpChannel::ReleaseMainThreadOnlyReferences(){if(NS_IsMainThread()){// Already on main thread, let dtor to// take care of releasing referencesreturn;}nsTArray<nsCOMPtr<nsISupports>>arrayToRelease;arrayToRelease.AppendElement(mApplicationCacheForWrite.forget());arrayToRelease.AppendElement(mAuthProvider.forget());arrayToRelease.AppendElement(mRedirectURI.forget());arrayToRelease.AppendElement(mRedirectChannel.forget());arrayToRelease.AppendElement(mPreflightChannel.forget());NS_DispatchToMainThread(newProxyReleaseRunnable(Move(arrayToRelease)));}nsresultnsHttpChannel::Init(nsIURI*uri,uint32_tcaps,nsProxyInfo*proxyInfo,uint32_tproxyResolveFlags,nsIURI*proxyURI,uint64_tchannelId){nsresultrv=HttpBaseChannel::Init(uri,caps,proxyInfo,proxyResolveFlags,proxyURI,channelId);if(NS_FAILED(rv))returnrv;LOG(("nsHttpChannel::Init [this=%p]\n",this));returnrv;}nsresultnsHttpChannel::AddSecurityMessage(constnsAString&aMessageTag,constnsAString&aMessageCategory){if(mWarningReporter){returnmWarningReporter->ReportSecurityMessage(aMessageTag,aMessageCategory);}returnHttpBaseChannel::AddSecurityMessage(aMessageTag,aMessageCategory);}//-----------------------------------------------------------------------------// nsHttpChannel <private>//-----------------------------------------------------------------------------nsresultnsHttpChannel::Connect(){nsresultrv;LOG(("nsHttpChannel::Connect [this=%p]\n",this));// Note that we are only setting the "Upgrade-Insecure-Requests" request// header for *all* navigational requests instead of all requests as// defined in the spec, see:// https://www.w3.org/TR/upgrade-insecure-requests/#preferencensContentPolicyTypetype=mLoadInfo?mLoadInfo->GetExternalContentPolicyType():nsIContentPolicy::TYPE_OTHER;if(type==nsIContentPolicy::TYPE_DOCUMENT||type==nsIContentPolicy::TYPE_SUBDOCUMENT){rv=SetRequestHeader(NS_LITERAL_CSTRING("Upgrade-Insecure-Requests"),NS_LITERAL_CSTRING("1"),false);NS_ENSURE_SUCCESS(rv,rv);}boolisHttps=false;rv=mURI->SchemeIs("https",&isHttps);NS_ENSURE_SUCCESS(rv,rv);nsCOMPtr<nsIPrincipal>resultPrincipal;if(!isHttps&&mLoadInfo){nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(this,getter_AddRefs(resultPrincipal));}OriginAttributesoriginAttributes;if(!NS_GetOriginAttributes(this,originAttributes)){returnNS_ERROR_FAILURE;}boolisHttp=false;rv=mURI->SchemeIs("http",&isHttp);NS_ENSURE_SUCCESS(rv,rv);if(isHttp){boolshouldUpgrade=false;rv=NS_ShouldSecureUpgrade(mURI,mLoadInfo,resultPrincipal,mPrivateBrowsing,mAllowSTS,originAttributes,shouldUpgrade);NS_ENSURE_SUCCESS(rv,rv);if(shouldUpgrade){returnAsyncCall(&nsHttpChannel::HandleAsyncRedirectChannelToHttps);}}// ensure that we are using a valid hostnameif(!net_IsValidHostName(nsDependentCString(mConnectionInfo->Origin())))returnNS_ERROR_UNKNOWN_HOST;if(mUpgradeProtocolCallback){mCaps|=NS_HTTP_DISALLOW_SPDY;}// Finalize ConnectionInfo flags before SpeculativeConnectmConnectionInfo->SetAnonymous((mLoadFlags&LOAD_ANONYMOUS)!=0);mConnectionInfo->SetPrivate(mPrivateBrowsing);mConnectionInfo->SetNoSpdy(mCaps&NS_HTTP_DISALLOW_SPDY);mConnectionInfo->SetBeConservative((mCaps&NS_HTTP_BE_CONSERVATIVE)||mBeConservative);// Consider opening a TCP connection right away.SpeculativeConnect();// Don't allow resuming when cache must be usedif(mResuming&&(mLoadFlags&LOAD_ONLY_FROM_CACHE)){LOG(("Resuming from cache is not supported yet"));returnNS_ERROR_DOCUMENT_NOT_CACHED;}// open a cache entry for this channel...rv=OpenCacheEntry(isHttps);// do not continue if asyncOpenCacheEntry is in progressif(AwaitingCacheCallbacks()){LOG(("nsHttpChannel::Connect %p AwaitingCacheCallbacks forces async\n",this));MOZ_ASSERT(NS_SUCCEEDED(rv),"Unexpected state");if(mNetworkTriggered&&mWaitingForProxy){// Someone has called TriggerNetwork(), meaning we are racing the// network with the cache.mWaitingForProxy=false;returnTryHSTSPriming();}returnNS_OK;}if(NS_FAILED(rv)){LOG(("OpenCacheEntry failed [rv=%"PRIx32"]\n",static_cast<uint32_t>(rv)));// if this channel is only allowed to pull from the cache, then// we must fail if we were unable to open a cache entry.if(mLoadFlags&LOAD_ONLY_FROM_CACHE){// If we have a fallback URI (and we're not already// falling back), process the fallback asynchronously.if(!mFallbackChannel&&!mFallbackKey.IsEmpty()){returnAsyncCall(&nsHttpChannel::HandleAsyncFallback);}returnNS_ERROR_DOCUMENT_NOT_CACHED;}// otherwise, let's just proceed without using the cache.}returnTriggerNetwork(0);}nsresultnsHttpChannel::TryHSTSPriming(){boolisHttpScheme;nsresultrv=mURI->SchemeIs("http",&isHttpScheme);NS_ENSURE_SUCCESS(rv,rv);boolisHttpsScheme;rv=mURI->SchemeIs("https",&isHttpsScheme);NS_ENSURE_SUCCESS(rv,rv);if((isHttpScheme||isHttpsScheme)&&mLoadInfo){if(mLoadInfo->GetIsHSTSPriming()){// shortcut priming requests so they don't get countedreturnContinueConnect();}// HSTS priming requires the LoadInfo provided with AsyncOpen2boolrequireHSTSPriming=mLoadInfo->GetForceHSTSPriming();if(requireHSTSPriming&&nsMixedContentBlocker::sSendHSTSPriming&&mInterceptCache==DO_NOT_INTERCEPT){if(!isHttpsScheme){rv=HSTSPrimingListener::StartHSTSPriming(this,this);if(NS_FAILED(rv)){CloseCacheEntry(false);Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_REQUESTS,HSTSPrimingRequest::eHSTS_PRIMING_REQUEST_ERROR);returnrv;}returnNS_OK;}if(!mLoadInfo->GetIsHSTSPrimingUpgrade()){// The request was already upgraded, for example by a prior// successful priming requestLOG(("HSTS Priming: request already upgraded"));Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_RESULT,HSTSPrimingResult::eHSTS_PRIMING_ALREADY_UPGRADED);// No HSTS Priming request was sent.Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_REQUESTS,HSTSPrimingRequest::eHSTS_PRIMING_REQUEST_ALREADY_UPGRADED);}mLoadInfo->ClearHSTSPriming();returnContinueConnect();}if(!mLoadInfo->GetIsHSTSPrimingUpgrade()){// No HSTS Priming request was sent, and we didn't already record this requestTelemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_REQUESTS,HSTSPrimingRequest::eHSTS_PRIMING_NO_REQUEST);}}else{Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_REQUESTS,HSTSPrimingRequest::eHSTS_PRIMING_REQUEST_NO_LOAD_INFO);}returnContinueConnect();}// nsIInputAvailableCallback (nsIStreamTransportService.idl)NS_IMETHODIMPnsHttpChannel::OnInputAvailableComplete(uint64_tsize,nsresultstatus){MOZ_ASSERT(NS_IsMainThread(),"Wrong thread.");LOG(("nsHttpChannel::OnInputAvailableComplete %p %"PRIx32"\n",this,static_cast<uint32_t>(status)));if(NS_SUCCEEDED(status)){mReqContentLength=size;}else{// fall back to synchronous on the error path. should not happen.if(NS_SUCCEEDED(mUploadStream->Available(&size))){mReqContentLength=size;}}LOG(("nsHttpChannel::DetermineContentLength %p from sts\n",this));mReqContentLengthDetermined=1;nsresultrv=mCanceled?mStatus:ContinueConnect();if(NS_FAILED(rv)){CloseCacheEntry(false);Unused<<AsyncAbort(rv);}returnNS_OK;}// nsIFileStream needs to be sent to a worker thread// to do Available() as it may cause disk/IO. Unfortunately// we have to look at the streams wrapped by a few other// abstractions to be sure.staticboolisFileStream(nsIInputStream*stream){if(!stream){returnfalse;}nsCOMPtr<nsIFileInputStream>fileStream=do_QueryInterface(stream);if(fileStream){returntrue;}nsCOMPtr<nsIBufferedInputStream>bufferedStream=do_QueryInterface(stream);if(bufferedStream){nsCOMPtr<nsIInputStream>innerStream;if(NS_SUCCEEDED(bufferedStream->GetData(getter_AddRefs(innerStream)))){returnisFileStream(innerStream);}}nsCOMPtr<nsIMIMEInputStream>mimeStream=do_QueryInterface(stream);if(mimeStream){nsCOMPtr<nsIInputStream>innerStream;if(NS_SUCCEEDED(mimeStream->GetData(getter_AddRefs(innerStream)))){returnisFileStream(innerStream);}}nsCOMPtr<nsIMultiplexInputStream>muxStream=do_QueryInterface(stream);uint32_tmuxCount=0;if(muxStream){muxStream->GetCount(&muxCount);for(uint32_ti=0;i<muxCount;++i){nsCOMPtr<nsIInputStream>subStream;if(NS_SUCCEEDED(muxStream->GetStream(i,getter_AddRefs(subStream)))&&isFileStream(subStream)){returntrue;}}}returnfalse;}voidnsHttpChannel::DetermineContentLength(){nsCOMPtr<nsIStreamTransportService>sts(services::GetStreamTransportService());if(!mUploadStream||!sts){LOG(("nsHttpChannel::DetermineContentLength %p no body\n",this));mReqContentLength=0U;mReqContentLengthDetermined=1;return;}if(!isFileStream(mUploadStream)){mUploadStream->Available(&mReqContentLength);LOG(("nsHttpChannel::DetermineContentLength %p from mem\n",this));mReqContentLengthDetermined=1;return;}LOG(("nsHttpChannel::DetermineContentLength Async [this=%p]\n",this));sts->InputAvailable(mUploadStream,this);}nsresultnsHttpChannel::ContinueConnect(){// If we have a request body that is going to require bouncing to the STS// in order to determine the content-length as doing it on the main thread// will incur file IO some of the time.if(!mReqContentLengthDetermined){// C-L might be determined sync or async. Sync will set// mReqContentLengthDetermined to true in DetermineContentLength()DetermineContentLength();}if(!mReqContentLengthDetermined){returnNS_OK;}// If we have had HSTS priming, we need to reevaluate whether we need// a CORS preflight. Bug: 1272440// If we need to start a CORS preflight, do it now!// Note that it is important to do this before the early returns below.if(!mIsCorsPreflightDone&&mRequireCORSPreflight&&mInterceptCache!=INTERCEPTED){MOZ_ASSERT(!mPreflightChannel);nsresultrv=nsCORSListenerProxy::StartCORSPreflight(this,this,mUnsafeHeaders,getter_AddRefs(mPreflightChannel));returnrv;}MOZ_RELEASE_ASSERT(!(mRequireCORSPreflight&&mInterceptCache!=INTERCEPTED)||mIsCorsPreflightDone,"CORS preflight must have been finished by the time we ""do the rest of ContinueConnect");// we may or may not have a cache entry at this pointif(mCacheEntry){// read straight from the cache if possible...if(mCachedContentIsValid){nsRunnableMethod<nsHttpChannel>*event=nullptr;nsresultrv;if(!mCachedContentIsPartial){rv=AsyncCall(&nsHttpChannel::AsyncOnExamineCachedResponse,&event);if(NS_FAILED(rv)){LOG((" AsyncCall failed (%08x)",static_cast<uint32_t>(rv)));}}rv=ReadFromCache(true);if(NS_FAILED(rv)&&event){event->Revoke();}// Don't accumulate the cache hit telemetry for intercepted channels.if(mInterceptCache!=INTERCEPTED){AccumulateCacheHitTelemetry(kCacheHit);}returnrv;}elseif(mLoadFlags&LOAD_ONLY_FROM_CACHE){// the cache contains the requested resource, but it must be// validated before we can reuse it. since we are not allowed// to hit the net, there's nothing more to do. the document// is effectively not in the cache.LOG((" !mCachedContentIsValid && mLoadFlags & LOAD_ONLY_FROM_CACHE"));returnNS_ERROR_DOCUMENT_NOT_CACHED;}}elseif(mLoadFlags&LOAD_ONLY_FROM_CACHE){// If we have a fallback URI (and we're not already// falling back), process the fallback asynchronously.if(!mFallbackChannel&&!mFallbackKey.IsEmpty()){returnAsyncCall(&nsHttpChannel::HandleAsyncFallback);}LOG((" !mCacheEntry && mLoadFlags & LOAD_ONLY_FROM_CACHE"));returnNS_ERROR_DOCUMENT_NOT_CACHED;}if(mLoadFlags&LOAD_NO_NETWORK_IO){LOG((" mLoadFlags & LOAD_NO_NETWORK_IO"));returnNS_ERROR_DOCUMENT_NOT_CACHED;}// hit the net...nsresultrv=SetupTransaction();if(NS_FAILED(rv))returnrv;rv=gHttpHandler->InitiateTransaction(mTransaction,mPriority);if(NS_FAILED(rv))returnrv;rv=mTransactionPump->AsyncRead(this,nullptr);if(NS_FAILED(rv))returnrv;uint32_tsuspendCount=mSuspendCount;while(suspendCount--)mTransactionPump->Suspend();returnNS_OK;}voidnsHttpChannel::SpeculativeConnect(){// Before we take the latency hit of dealing with the cache, try and// get the TCP (and SSL) handshakes going so they can overlap.// don't speculate if we are on a local blocklist, on uses of the offline// application cache, if we are offline, when doing http upgrade (i.e.// websockets bootstrap), or if we can't do keep-alive (because then we// couldn't reuse the speculative connection anyhow).if(mLocalBlocklist||mApplicationCache||gIOService->IsOffline()||mUpgradeProtocolCallback||!(mCaps&NS_HTTP_ALLOW_KEEPALIVE))return;// LOAD_ONLY_FROM_CACHE and LOAD_NO_NETWORK_IO must not hit network.// LOAD_FROM_CACHE and LOAD_CHECK_OFFLINE_CACHE are unlikely to hit network,// so skip preconnects for them.if(mLoadFlags&(LOAD_ONLY_FROM_CACHE|LOAD_FROM_CACHE|LOAD_NO_NETWORK_IO|LOAD_CHECK_OFFLINE_CACHE))return;if(mAllowStaleCacheContent){return;}nsCOMPtr<nsIInterfaceRequestor>callbacks;NS_NewNotificationCallbacksAggregation(mCallbacks,mLoadGroup,getter_AddRefs(callbacks));if(!callbacks)return;Unused<<gHttpHandler->SpeculativeConnect(mConnectionInfo,callbacks,mCaps&NS_HTTP_DISALLOW_SPDY);}voidnsHttpChannel::DoNotifyListenerCleanup(){// We don't need this info anymoreCleanRedirectCacheChainIfNecessary();}voidnsHttpChannel::ReleaseListeners(){HttpBaseChannel::ReleaseListeners();mChannelClassifier=nullptr;}voidnsHttpChannel::HandleAsyncRedirect(){NS_PRECONDITION(!mCallOnResume,"How did that happen?");if(mSuspendCount){LOG(("Waiting until resume to do async redirect [this=%p]\n",this));mCallOnResume=&nsHttpChannel::HandleAsyncRedirect;return;}nsresultrv=NS_OK;LOG(("nsHttpChannel::HandleAsyncRedirect [this=%p]\n",this));// since this event is handled asynchronously, it is possible that this// channel could have been canceled, in which case there would be no point// in processing the redirect.if(NS_SUCCEEDED(mStatus)){PushRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncRedirect);rv=AsyncProcessRedirection(mResponseHead->Status());if(NS_FAILED(rv)){PopRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncRedirect);// TODO: if !DoNotRender3xxBody(), render redirect body instead.// But first we need to cache 3xx bodies (bug 748510)rv=ContinueHandleAsyncRedirect(rv);MOZ_ASSERT(NS_SUCCEEDED(rv));}}else{rv=ContinueHandleAsyncRedirect(mStatus);MOZ_ASSERT(NS_SUCCEEDED(rv));}}nsresultnsHttpChannel::ContinueHandleAsyncRedirect(nsresultrv){if(NS_FAILED(rv)){// If AsyncProcessRedirection fails, then we have to send out the// OnStart/OnStop notifications.LOG(("ContinueHandleAsyncRedirect got failure result [rv=%"PRIx32"]\n",static_cast<uint32_t>(rv)));boolredirectsEnabled=!mLoadInfo||!mLoadInfo->GetDontFollowRedirects();if(redirectsEnabled){// TODO: stop failing original channel if redirect vetoed?mStatus=rv;DoNotifyListener();// Blow away cache entry if we couldn't process the redirect// for some reason (the cache entry might be corrupt).if(mCacheEntry){mCacheEntry->AsyncDoom(nullptr);}}else{DoNotifyListener();}}CloseCacheEntry(true);mIsPending=false;if(mLoadGroup)mLoadGroup->RemoveRequest(this,nullptr,mStatus);returnNS_OK;}voidnsHttpChannel::HandleAsyncNotModified(){NS_PRECONDITION(!mCallOnResume,"How did that happen?");if(mSuspendCount){LOG(("Waiting until resume to do async not-modified [this=%p]\n",this));mCallOnResume=&nsHttpChannel::HandleAsyncNotModified;return;}LOG(("nsHttpChannel::HandleAsyncNotModified [this=%p]\n",this));DoNotifyListener();CloseCacheEntry(false);mIsPending=false;if(mLoadGroup)mLoadGroup->RemoveRequest(this,nullptr,mStatus);}voidnsHttpChannel::HandleAsyncFallback(){NS_PRECONDITION(!mCallOnResume,"How did that happen?");if(mSuspendCount){LOG(("Waiting until resume to do async fallback [this=%p]\n",this));mCallOnResume=&nsHttpChannel::HandleAsyncFallback;return;}nsresultrv=NS_OK;LOG(("nsHttpChannel::HandleAsyncFallback [this=%p]\n",this));// since this event is handled asynchronously, it is possible that this// channel could have been canceled, in which case there would be no point// in processing the fallback.if(!mCanceled){PushRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncFallback);boolwaitingForRedirectCallback;rv=ProcessFallback(&waitingForRedirectCallback);if(waitingForRedirectCallback)return;PopRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncFallback);}rv=ContinueHandleAsyncFallback(rv);MOZ_ASSERT(NS_SUCCEEDED(rv));}nsresultnsHttpChannel::ContinueHandleAsyncFallback(nsresultrv){if(!mCanceled&&(NS_FAILED(rv)||!mFallingBack)){// If ProcessFallback fails, then we have to send out the// OnStart/OnStop notifications.LOG(("ProcessFallback failed [rv=%"PRIx32", %d]\n",static_cast<uint32_t>(rv),mFallingBack));mStatus=NS_FAILED(rv)?rv:NS_ERROR_DOCUMENT_NOT_CACHED;DoNotifyListener();}mIsPending=false;if(mLoadGroup)mLoadGroup->RemoveRequest(this,nullptr,mStatus);returnrv;}voidnsHttpChannel::SetupTransactionRequestContext(){if(!EnsureRequestContextID()){return;}nsIRequestContextService*rcsvc=gHttpHandler->GetRequestContextService();if(!rcsvc){return;}nsCOMPtr<nsIRequestContext>rc;nsresultrv=rcsvc->GetRequestContext(mRequestContextID,getter_AddRefs(rc));if(NS_FAILED(rv)){return;}mTransaction->SetRequestContext(rc);}nsresultnsHttpChannel::SetupTransaction(){LOG(("nsHttpChannel::SetupTransaction [this=%p]\n",this));NS_ENSURE_TRUE(!mTransaction,NS_ERROR_ALREADY_INITIALIZED);nsresultrv;mUsedNetwork=1;if(!mAllowSpdy){mCaps|=NS_HTTP_DISALLOW_SPDY;}if(mBeConservative){mCaps|=NS_HTTP_BE_CONSERVATIVE;}// Use the URI path if not proxying (transparent proxying such as proxy// CONNECT does not count here). Also figure out what HTTP version to use.nsAutoCStringbuf,path;nsCString*requestURI;// This is the normal e2e H1 path syntax "/index.html"rv=mURI->GetPath(path);if(NS_FAILED(rv)){returnrv;}// path may contain UTF-8 characters, so ensure that they're escaped.if(NS_EscapeURL(path.get(),path.Length(),esc_OnlyNonASCII,buf)){requestURI=&buf;}else{requestURI=&path;}// trim off the #ref portion if any...int32_tref1=requestURI->FindChar('#');if(ref1!=kNotFound){requestURI->SetLength(ref1);}if(mConnectionInfo->UsingConnect()||!mConnectionInfo->UsingHttpProxy()){mRequestHead.SetVersion(gHttpHandler->HttpVersion());}else{mRequestHead.SetPath(*requestURI);// RequestURI should be the absolute uri H1 proxy syntax "http://foo/index.html"// so we will overwrite the relative version in requestURIrv=mURI->GetUserPass(buf);if(NS_FAILED(rv))returnrv;if(!buf.IsEmpty()&&((strncmp(mSpec.get(),"http:",5)==0)||strncmp(mSpec.get(),"https:",6)==0)){nsCOMPtr<nsIURI>tempURI;rv=mURI->Clone(getter_AddRefs(tempURI));if(NS_FAILED(rv))returnrv;rv=tempURI->SetUserPass(EmptyCString());if(NS_FAILED(rv))returnrv;rv=tempURI->GetAsciiSpec(path);if(NS_FAILED(rv))returnrv;requestURI=&path;}else{requestURI=&mSpec;}// trim off the #ref portion if any...int32_tref2=requestURI->FindChar('#');if(ref2!=kNotFound){requestURI->SetLength(ref2);}mRequestHead.SetVersion(gHttpHandler->ProxyHttpVersion());}mRequestHead.SetRequestURI(*requestURI);// set the request time for cache expiration calculationsmRequestTime=NowInSeconds();mRequestTimeInitialized=true;// if doing a reload, force end-to-endif(mLoadFlags&LOAD_BYPASS_CACHE){// We need to send 'Pragma:no-cache' to inhibit proxy caching even if// no proxy is configured since we might be talking with a transparent// proxy, i.e. one that operates at the network level. See bug #14772.rv=mRequestHead.SetHeaderOnce(nsHttp::Pragma,"no-cache",true);MOZ_ASSERT(NS_SUCCEEDED(rv));// If we're configured to speak HTTP/1.1 then also send 'Cache-control:// no-cache'if(mRequestHead.Version()>=NS_HTTP_VERSION_1_1){rv=mRequestHead.SetHeaderOnce(nsHttp::Cache_Control,"no-cache",true);MOZ_ASSERT(NS_SUCCEEDED(rv));}}elseif((mLoadFlags&VALIDATE_ALWAYS)&&!mCacheEntryIsWriteOnly){// We need to send 'Cache-Control: max-age=0' to force each cache along// the path to the origin server to revalidate its own entry, if any,// with the next cache or server. See bug #84847.//// If we're configured to speak HTTP/1.0 then just send 'Pragma: no-cache'if(mRequestHead.Version()>=NS_HTTP_VERSION_1_1)rv=mRequestHead.SetHeaderOnce(nsHttp::Cache_Control,"max-age=0",true);elserv=mRequestHead.SetHeaderOnce(nsHttp::Pragma,"no-cache",true);MOZ_ASSERT(NS_SUCCEEDED(rv));}if(mResuming){charbyteRange[32];SprintfLiteral(byteRange,"bytes=%"PRIu64"-",mStartPos);rv=mRequestHead.SetHeader(nsHttp::Range,nsDependentCString(byteRange));MOZ_ASSERT(NS_SUCCEEDED(rv));if(!mEntityID.IsEmpty()){// Also, we want an error if this resource changed in the meantime// Format of the entity id is: escaped_etag/size/lastmodnsCString::const_iteratorstart,end,slash;mEntityID.BeginReading(start);mEntityID.EndReading(end);mEntityID.BeginReading(slash);if(FindCharInReadable('/',slash,end)){nsAutoCStringifMatch;rv=mRequestHead.SetHeader(nsHttp::If_Match,NS_UnescapeURL(Substring(start,slash),0,ifMatch));MOZ_ASSERT(NS_SUCCEEDED(rv));++slash;// Incrementing, so that searching for '/' won't find// the same slash again}if(FindCharInReadable('/',slash,end)){rv=mRequestHead.SetHeader(nsHttp::If_Unmodified_Since,Substring(++slash,end));MOZ_ASSERT(NS_SUCCEEDED(rv));}}}// create wrapper for this channel's notification callbacksnsCOMPtr<nsIInterfaceRequestor>callbacks;NS_NewNotificationCallbacksAggregation(mCallbacks,mLoadGroup,getter_AddRefs(callbacks));// create the transaction objectmTransaction=newnsHttpTransaction();LOG(("nsHttpChannel %p created nsHttpTransaction %p\n",this,mTransaction.get()));mTransaction->SetTransactionObserver(mTransactionObserver);mTransactionObserver=nullptr;// See bug #466080. Transfer LOAD_ANONYMOUS flag to socket-layer.if(mLoadFlags&LOAD_ANONYMOUS)mCaps|=NS_HTTP_LOAD_ANONYMOUS;if(mTimingEnabled)mCaps|=NS_HTTP_TIMING_ENABLED;if(mUpgradeProtocolCallback){rv=mRequestHead.SetHeader(nsHttp::Upgrade,mUpgradeProtocol,false);MOZ_ASSERT(NS_SUCCEEDED(rv));rv=mRequestHead.SetHeaderOnce(nsHttp::Connection,nsHttp::Upgrade.get(),true);MOZ_ASSERT(NS_SUCCEEDED(rv));mCaps|=NS_HTTP_STICKY_CONNECTION;mCaps&=~NS_HTTP_ALLOW_KEEPALIVE;}if(mPushedStream){mTransaction->SetPushedStream(mPushedStream);mPushedStream=nullptr;}nsCOMPtr<nsIHttpPushListener>pushListener;NS_QueryNotificationCallbacks(mCallbacks,mLoadGroup,NS_GET_IID(nsIHttpPushListener),getter_AddRefs(pushListener));if(pushListener){mCaps|=NS_HTTP_ONPUSH_LISTENER;}EnsureTopLevelOuterContentWindowId();nsCOMPtr<nsIAsyncInputStream>responseStream;rv=mTransaction->Init(mCaps,mConnectionInfo,&mRequestHead,mUploadStream,mReqContentLength,mUploadStreamHasHeaders,GetCurrentThreadEventTarget(),callbacks,this,mTopLevelOuterContentWindowId,getter_AddRefs(responseStream));if(NS_FAILED(rv)){mTransaction=nullptr;returnrv;}mTransaction->SetClassOfService(mClassOfService);SetupTransactionRequestContext();rv=nsInputStreamPump::Create(getter_AddRefs(mTransactionPump),responseStream);returnrv;}// NOTE: This function duplicates code from nsBaseChannel. This will go away// once HTTP uses nsBaseChannel (part of bug 312760)staticvoidCallTypeSniffers(void*aClosure,constuint8_t*aData,uint32_taCount){nsIChannel*chan=static_cast<nsIChannel*>(aClosure);nsAutoCStringnewType;NS_SniffContent(NS_CONTENT_SNIFFER_CATEGORY,chan,aData,aCount,newType);if(!newType.IsEmpty()){chan->SetContentType(newType);}}// Helper Function to report messages to the console when loading// a resource was blocked due to a MIME type mismatch.voidReportTypeBlocking(nsIURI*aURI,nsILoadInfo*aLoadInfo,constchar*aMessageName){NS_ConvertUTF8toUTF16specUTF16(aURI->GetSpecOrDefault());constchar16_t*params[]={specUTF16.get()};nsCOMPtr<nsIDocument>doc;if(aLoadInfo){nsCOMPtr<nsIDOMDocument>domDoc;aLoadInfo->GetLoadingDocument(getter_AddRefs(domDoc));if(domDoc){doc=do_QueryInterface(domDoc);}}nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,NS_LITERAL_CSTRING("MIMEMISMATCH"),doc,nsContentUtils::eSECURITY_PROPERTIES,aMessageName,params,ArrayLength(params));}// Check and potentially enforce X-Content-Type-Options: nosniffnsresultProcessXCTO(nsIURI*aURI,nsHttpResponseHead*aResponseHead,nsILoadInfo*aLoadInfo){if(!aURI||!aResponseHead||!aLoadInfo){// if there is no uri, no response head or no loadInfo, then there is nothing to doreturnNS_OK;}// 1) Query the XCTO header and check if 'nosniff' is the first value.nsAutoCStringcontentTypeOptionsHeader;Unused<<aResponseHead->GetHeader(nsHttp::X_Content_Type_Options,contentTypeOptionsHeader);if(contentTypeOptionsHeader.IsEmpty()){// if there is no XCTO header, then there is nothing to do.returnNS_OK;}// XCTO header might contain multiple values which are comma separated, so:// a) let's skip all subsequent values// e.g. " NoSniFF , foo " will be " NoSniFF "int32_tidx=contentTypeOptionsHeader.Find(",");if(idx>0){contentTypeOptionsHeader=Substring(contentTypeOptionsHeader,0,idx);}// b) let's trim all surrounding whitespace// e.g. " NoSniFF " -> "NoSniFF"contentTypeOptionsHeader.StripWhitespace();// c) let's compare the header (ignoring case)// e.g. "NoSniFF" -> "nosniff"// if it's not 'nosniff' then there is nothing to do hereif(!contentTypeOptionsHeader.EqualsIgnoreCase("nosniff")){// since we are getting here, the XCTO header was sent;// a non matching value most likely means a mistake happenend;// e.g. sending 'nosnif' instead of 'nosniff', let's log a warning.NS_ConvertUTF8toUTF16char16_header(contentTypeOptionsHeader);constchar16_t*params[]={char16_header.get()};nsCOMPtr<nsIDocument>doc;nsCOMPtr<nsIDOMDocument>domDoc;aLoadInfo->GetLoadingDocument(getter_AddRefs(domDoc));if(domDoc){doc=do_QueryInterface(domDoc);}nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,NS_LITERAL_CSTRING("XCTO"),doc,nsContentUtils::eSECURITY_PROPERTIES,"XCTOHeaderValueMissing",params,ArrayLength(params));returnNS_OK;}// 2) Query the content type from the channelnsAutoCStringcontentType;aResponseHead->ContentType(contentType);// 3) Compare the expected MIME type with the actual typeif(aLoadInfo->GetExternalContentPolicyType()==nsIContentPolicy::TYPE_STYLESHEET){if(contentType.EqualsLiteral(TEXT_CSS)){returnNS_OK;}ReportTypeBlocking(aURI,aLoadInfo,"MimeTypeMismatch");returnNS_ERROR_CORRUPTED_CONTENT;}if(aLoadInfo->GetExternalContentPolicyType()==nsIContentPolicy::TYPE_IMAGE){if(StringBeginsWith(contentType,NS_LITERAL_CSTRING("image/"))){Accumulate(Telemetry::XCTO_NOSNIFF_BLOCK_IMAGE,0);returnNS_OK;}Accumulate(Telemetry::XCTO_NOSNIFF_BLOCK_IMAGE,1);// Instead of consulting Preferences::GetBool() all the time we// can cache the result to speed things up.staticboolsXCTONosniffBlockImages=false;staticboolsIsInited=false;if(!sIsInited){sIsInited=true;Preferences::AddBoolVarCache(&sXCTONosniffBlockImages,"security.xcto_nosniff_block_images");}if(!sXCTONosniffBlockImages){returnNS_OK;}ReportTypeBlocking(aURI,aLoadInfo,"MimeTypeMismatch");returnNS_ERROR_CORRUPTED_CONTENT;}if(aLoadInfo->GetExternalContentPolicyType()==nsIContentPolicy::TYPE_SCRIPT){if(nsContentUtils::IsScriptType(contentType)){returnNS_OK;}ReportTypeBlocking(aURI,aLoadInfo,"MimeTypeMismatch");returnNS_ERROR_CORRUPTED_CONTENT;}returnNS_OK;}// Ensure that a load of type script has correct MIME typensresultEnsureMIMEOfScript(nsIURI*aURI,nsHttpResponseHead*aResponseHead,nsILoadInfo*aLoadInfo){if(!aURI||!aResponseHead||!aLoadInfo){// if there is no uri, no response head or no loadInfo, then there is nothing to doreturnNS_OK;}if(aLoadInfo->GetExternalContentPolicyType()!=nsIContentPolicy::TYPE_SCRIPT){// if this is not a script load, then there is nothing to doreturnNS_OK;}nsAutoCStringcontentType;aResponseHead->ContentType(contentType);NS_ConvertUTF8toUTF16typeString(contentType);if(nsContentUtils::IsJavascriptMIMEType(typeString)){// script load has type scriptTelemetry::Accumulate(Telemetry::SCRIPT_BLOCK_INCORRECT_MIME,1);returnNS_OK;}boolblock=false;if(StringBeginsWith(contentType,NS_LITERAL_CSTRING("image/"))){// script load has type imageTelemetry::Accumulate(Telemetry::SCRIPT_BLOCK_INCORRECT_MIME,2);block=true;}elseif(StringBeginsWith(contentType,NS_LITERAL_CSTRING("audio/"))){// script load has type audioTelemetry::Accumulate(Telemetry::SCRIPT_BLOCK_INCORRECT_MIME,3);block=true;}elseif(StringBeginsWith(contentType,NS_LITERAL_CSTRING("video/"))){// script load has type videoTelemetry::Accumulate(Telemetry::SCRIPT_BLOCK_INCORRECT_MIME,4);block=true;}elseif(StringBeginsWith(contentType,NS_LITERAL_CSTRING("text/csv"))){// script load has type text/csvTelemetry::Accumulate(Telemetry::SCRIPT_BLOCK_INCORRECT_MIME,6);block=true;}if(block){// Instead of consulting Preferences::GetBool() all the time we// can cache the result to speed things up.staticboolsCachedBlockScriptWithWrongMime=false;staticboolsIsInited=false;if(!sIsInited){sIsInited=true;Preferences::AddBoolVarCache(&sCachedBlockScriptWithWrongMime,"security.block_script_with_wrong_mime");}// Do not block the load if the feature is not enabled.if(!sCachedBlockScriptWithWrongMime){returnNS_OK;}ReportTypeBlocking(aURI,aLoadInfo,"BlockScriptWithWrongMimeType");returnNS_ERROR_CORRUPTED_CONTENT;}if(StringBeginsWith(contentType,NS_LITERAL_CSTRING("text/plain"))){// script load has type text/plainTelemetry::Accumulate(Telemetry::SCRIPT_BLOCK_INCORRECT_MIME,5);returnNS_OK;}if(StringBeginsWith(contentType,NS_LITERAL_CSTRING("text/xml"))){// script load has type text/xmlTelemetry::Accumulate(Telemetry::SCRIPT_BLOCK_INCORRECT_MIME,7);returnNS_OK;}if(StringBeginsWith(contentType,NS_LITERAL_CSTRING("application/octet-stream"))){// script load has type application/octet-streamTelemetry::Accumulate(Telemetry::SCRIPT_BLOCK_INCORRECT_MIME,8);returnNS_OK;}if(StringBeginsWith(contentType,NS_LITERAL_CSTRING("application/xml"))){// script load has type application/xmlTelemetry::Accumulate(Telemetry::SCRIPT_BLOCK_INCORRECT_MIME,9);returnNS_OK;}if(StringBeginsWith(contentType,NS_LITERAL_CSTRING("text/html"))){// script load has type text/htmlTelemetry::Accumulate(Telemetry::SCRIPT_BLOCK_INCORRECT_MIME,10);returnNS_OK;}if(contentType.IsEmpty()){// script load has no typeTelemetry::Accumulate(Telemetry::SCRIPT_BLOCK_INCORRECT_MIME,11);returnNS_OK;}// script load has unknown typeTelemetry::Accumulate(Telemetry::SCRIPT_BLOCK_INCORRECT_MIME,0);returnNS_OK;}nsresultnsHttpChannel::CallOnStartRequest(){LOG(("nsHttpChannel::CallOnStartRequest [this=%p]",this));MOZ_RELEASE_ASSERT(!(mRequireCORSPreflight&&mInterceptCache!=INTERCEPTED)||mIsCorsPreflightDone,"CORS preflight must have been finished by the time we ""call OnStartRequest");if(mOnStartRequestCalled){// This can only happen when a range request loading rest of the data// after interrupted concurrent cache read asynchronously failed, e.g.// the response range bytes are not as expected or this channel has// been externally canceled.//// It's legal to bypass CallOnStartRequest for that case since we've// already called OnStartRequest on our listener and also added all// content converters before.MOZ_ASSERT(mConcurrentCacheAccess);LOG(("CallOnStartRequest already invoked before"));returnmStatus;}mTracingEnabled=false;// Ensure mListener->OnStartRequest will be invoked before exiting// this function.autoonStartGuard=MakeScopeExit([&]{LOG((" calling mListener->OnStartRequest by ScopeExit [this=%p, ""listener=%p]\n",this,mListener.get()));MOZ_ASSERT(!mOnStartRequestCalled);if(mListener){nsCOMPtr<nsIStreamListener>deleteProtector(mListener);deleteProtector->OnStartRequest(this,mListenerContext);}mOnStartRequestCalled=true;});nsresultrv=EnsureMIMEOfScript(mURI,mResponseHead,mLoadInfo);NS_ENSURE_SUCCESS(rv,rv);rv=ProcessXCTO(mURI,mResponseHead,mLoadInfo);NS_ENSURE_SUCCESS(rv,rv);// Allow consumers to override our content typeif(mLoadFlags&LOAD_CALL_CONTENT_SNIFFERS){// NOTE: We can have both a txn pump and a cache pump when the cache// content is partial. In that case, we need to read from the cache,// because that's the one that has the initial contents. If that fails// then give the transaction pump a shot.nsIChannel*thisChannel=static_cast<nsIChannel*>(this);booltypeSniffersCalled=false;if(mCachePump){typeSniffersCalled=NS_SUCCEEDED(mCachePump->PeekStream(CallTypeSniffers,thisChannel));}if(!typeSniffersCalled&&mTransactionPump){mTransactionPump->PeekStream(CallTypeSniffers,thisChannel);}}boolunknownDecoderStarted=false;if(mResponseHead&&!mResponseHead->HasContentType()){MOZ_ASSERT(mConnectionInfo,"Should have connection info here");if(!mContentTypeHint.IsEmpty())mResponseHead->SetContentType(mContentTypeHint);elseif(mResponseHead->Version()==NS_HTTP_VERSION_0_9&&mConnectionInfo->OriginPort()!=mConnectionInfo->DefaultPort())mResponseHead->SetContentType(NS_LITERAL_CSTRING(TEXT_PLAIN));else{// Uh-oh. We had better find out what type we are!nsCOMPtr<nsIStreamConverterService>serv;rv=gHttpHandler->GetStreamConverterService(getter_AddRefs(serv));// If we failed, we just fall through to the "normal" caseif(NS_SUCCEEDED(rv)){nsCOMPtr<nsIStreamListener>converter;rv=serv->AsyncConvertData(UNKNOWN_CONTENT_TYPE,"*/*",mListener,mListenerContext,getter_AddRefs(converter));if(NS_SUCCEEDED(rv)){mListener=converter;unknownDecoderStarted=true;}}}}if(mResponseHead&&!mResponseHead->HasContentCharset())mResponseHead->SetContentCharset(mContentCharsetHint);if(mResponseHead&&mCacheEntry){// If we have a cache entry, set its predicted size to TotalEntitySize to// avoid caching an entry that will exceed the max size limit.rv=mCacheEntry->SetPredictedDataSize(mResponseHead->TotalEntitySize());if(NS_ERROR_FILE_TOO_BIG==rv){// Don't throw the entry away, we will need it later.LOG((" entry too big"));}else{NS_ENSURE_SUCCESS(rv,rv);}}LOG((" calling mListener->OnStartRequest [this=%p, listener=%p]\n",this,mListener.get()));// About to call OnStartRequest, dismiss the guard object.onStartGuard.release();if(mListener){MOZ_ASSERT(!mOnStartRequestCalled,"We should not call OsStartRequest twice");nsCOMPtr<nsIStreamListener>deleteProtector(mListener);rv=deleteProtector->OnStartRequest(this,mListenerContext);mOnStartRequestCalled=true;if(NS_FAILED(rv))returnrv;}else{NS_WARNING("OnStartRequest skipped because of null listener");mOnStartRequestCalled=true;}// Install stream converter if required.// If we use unknownDecoder, stream converters will be installed later (in// nsUnknownDecoder) after OnStartRequest is called for the real listener.if(!unknownDecoderStarted){nsCOMPtr<nsIStreamListener>listener;nsISupports*ctxt=mListenerContext;rv=DoApplyContentConversions(mListener,getter_AddRefs(listener),ctxt);if(NS_FAILED(rv)){returnrv;}if(listener){mListener=listener;mCompressListener=listener;}}// if this channel is for a download, close off access to the cache.if(mCacheEntry&&mChannelIsForDownload){mCacheEntry->AsyncDoom(nullptr);// We must keep the cache entry in case of partial request.// Concurrent access is the same, we need the entry in// OnStopRequest.if(!mCachedContentIsPartial&&!mConcurrentCacheAccess)CloseCacheEntry(false);}if(!mCanceled){// create offline cache entry if offline caching was requestedif(ShouldUpdateOfflineCacheEntry()){LOG(("writing to the offline cache"));rv=InitOfflineCacheEntry();if(NS_FAILED(rv))returnrv;// InitOfflineCacheEntry may have closed mOfflineCacheEntryif(mOfflineCacheEntry){rv=InstallOfflineCacheListener();if(NS_FAILED(rv))returnrv;}}elseif(mApplicationCacheForWrite){LOG(("offline cache is up to date, not updating"));CloseOfflineCacheEntry();}}// Check for a Content-Signature header and inject mediator if the header is// requested and available.// If requested (mLoadInfo->GetVerifySignedContent), but not present, or// present but not valid, fail this channel and return// NS_ERROR_INVALID_SIGNATURE to indicate a signature error and trigger a// fallback load in nsDocShell.// Note that OnStartRequest has already been called on the target stream// listener at this point. We have to add the listener here that late to// ensure that it's the last listener and can thus block the load in// OnStopRequest.if(!mCanceled){rv=ProcessContentSignatureHeader(mResponseHead);if(NS_FAILED(rv)){LOG(("Content-signature verification failed.\n"));returnrv;}}returnNS_OK;}nsresultnsHttpChannel::ProcessFailedProxyConnect(uint32_thttpStatus){// Failure to set up a proxy tunnel via CONNECT means one of the following:// 1) Proxy wants authorization, or forbids.// 2) DNS at proxy couldn't resolve target URL.// 3) Proxy connection to target failed or timed out.// 4) Eve intercepted our CONNECT, and is replying with malicious HTML.//// Our current architecture would parse the proxy's response content with// the permission of the target URL. Given #4, we must avoid rendering the// body of the reply, and instead give the user a (hopefully helpful)// boilerplate error page, based on just the HTTP status of the reply.MOZ_ASSERT(mConnectionInfo->UsingConnect(),"proxy connect failed but not using CONNECT?");nsresultrv;switch(httpStatus){case300:case301:case302:case303:case307:case308:// Bad redirect: not top-level, or it's a POST, bad/missing Location,// or ProcessRedirect() failed for some other reason. Legal// redirects that fail because site not available, etc., are handled// elsewhere, in the regular codepath.rv=NS_ERROR_CONNECTION_REFUSED;break;case403:// HTTP/1.1: "Forbidden"case407:// ProcessAuthentication() failedcase501:// HTTP/1.1: "Not Implemented"// user sees boilerplate Mozilla "Proxy Refused Connection" page.rv=NS_ERROR_PROXY_CONNECTION_REFUSED;break;// Squid sends 404 if DNS fails (regular 404 from target is tunneled)case404:// HTTP/1.1: "Not Found"// RFC 2616: "some deployed proxies are known to return 400 or 500 when// DNS lookups time out." (Squid uses 500 if it runs out of sockets: so// we have a conflict here).case400:// HTTP/1.1 "Bad Request"case500:// HTTP/1.1: "Internal Server Error"/* User sees: "Address Not Found: Firefox can't find the server at * www.foo.com." */rv=NS_ERROR_UNKNOWN_HOST;break;case502:// HTTP/1.1: "Bad Gateway" (invalid resp from target server)// Squid returns 503 if target request fails for anything but DNS.case503:// HTTP/1.1: "Service Unavailable"/* User sees: "Failed to Connect: * Firefox can't establish a connection to the server at * www.foo.com. Though the site seems valid, the browser * was unable to establish a connection." */rv=NS_ERROR_CONNECTION_REFUSED;break;// RFC 2616 uses 504 for both DNS and target timeout, so not clear what to// do here: picking target timeout, as DNS covered by 400/404/500case504:// HTTP/1.1: "Gateway Timeout"// user sees: "Network Timeout: The server at www.foo.com// is taking too long to respond."rv=NS_ERROR_NET_TIMEOUT;break;// Confused proxy server or malicious responsedefault:rv=NS_ERROR_PROXY_CONNECTION_REFUSED;break;}LOG(("Cancelling failed proxy CONNECT [this=%p httpStatus=%u]\n",this,httpStatus));Cancel(rv);{nsresultrv=CallOnStartRequest();if(NS_FAILED(rv)){LOG(("CallOnStartRequest failed [this=%p httpStatus=%u rv=%08x]\n",this,httpStatus,static_cast<uint32_t>(rv)));}}returnrv;}staticvoidGetSTSConsoleErrorTag(uint32_tfailureResult,nsAString&consoleErrorTag){switch(failureResult){casensISiteSecurityService::ERROR_UNTRUSTWORTHY_CONNECTION:consoleErrorTag=NS_LITERAL_STRING("STSUntrustworthyConnection");break;casensISiteSecurityService::ERROR_COULD_NOT_PARSE_HEADER:consoleErrorTag=NS_LITERAL_STRING("STSCouldNotParseHeader");break;casensISiteSecurityService::ERROR_NO_MAX_AGE:consoleErrorTag=NS_LITERAL_STRING("STSNoMaxAge");break;casensISiteSecurityService::ERROR_MULTIPLE_MAX_AGES:consoleErrorTag=NS_LITERAL_STRING("STSMultipleMaxAges");break;casensISiteSecurityService::ERROR_INVALID_MAX_AGE:consoleErrorTag=NS_LITERAL_STRING("STSInvalidMaxAge");break;casensISiteSecurityService::ERROR_MULTIPLE_INCLUDE_SUBDOMAINS:consoleErrorTag=NS_LITERAL_STRING("STSMultipleIncludeSubdomains");break;casensISiteSecurityService::ERROR_INVALID_INCLUDE_SUBDOMAINS:consoleErrorTag=NS_LITERAL_STRING("STSInvalidIncludeSubdomains");break;casensISiteSecurityService::ERROR_COULD_NOT_SAVE_STATE:consoleErrorTag=NS_LITERAL_STRING("STSCouldNotSaveState");break;default:consoleErrorTag=NS_LITERAL_STRING("STSUnknownError");break;}}staticvoidGetPKPConsoleErrorTag(uint32_tfailureResult,nsAString&consoleErrorTag){switch(failureResult){casensISiteSecurityService::ERROR_UNTRUSTWORTHY_CONNECTION:consoleErrorTag=NS_LITERAL_STRING("PKPUntrustworthyConnection");break;casensISiteSecurityService::ERROR_COULD_NOT_PARSE_HEADER:consoleErrorTag=NS_LITERAL_STRING("PKPCouldNotParseHeader");break;casensISiteSecurityService::ERROR_NO_MAX_AGE:consoleErrorTag=NS_LITERAL_STRING("PKPNoMaxAge");break;casensISiteSecurityService::ERROR_MULTIPLE_MAX_AGES:consoleErrorTag=NS_LITERAL_STRING("PKPMultipleMaxAges");break;casensISiteSecurityService::ERROR_INVALID_MAX_AGE:consoleErrorTag=NS_LITERAL_STRING("PKPInvalidMaxAge");break;casensISiteSecurityService::ERROR_MULTIPLE_INCLUDE_SUBDOMAINS:consoleErrorTag=NS_LITERAL_STRING("PKPMultipleIncludeSubdomains");break;casensISiteSecurityService::ERROR_INVALID_INCLUDE_SUBDOMAINS:consoleErrorTag=NS_LITERAL_STRING("PKPInvalidIncludeSubdomains");break;casensISiteSecurityService::ERROR_INVALID_PIN:consoleErrorTag=NS_LITERAL_STRING("PKPInvalidPin");break;casensISiteSecurityService::ERROR_MULTIPLE_REPORT_URIS:consoleErrorTag=NS_LITERAL_STRING("PKPMultipleReportURIs");break;casensISiteSecurityService::ERROR_PINSET_DOES_NOT_MATCH_CHAIN:consoleErrorTag=NS_LITERAL_STRING("PKPPinsetDoesNotMatch");break;casensISiteSecurityService::ERROR_NO_BACKUP_PIN:consoleErrorTag=NS_LITERAL_STRING("PKPNoBackupPin");break;casensISiteSecurityService::ERROR_COULD_NOT_SAVE_STATE:consoleErrorTag=NS_LITERAL_STRING("PKPCouldNotSaveState");break;casensISiteSecurityService::ERROR_ROOT_NOT_BUILT_IN:consoleErrorTag=NS_LITERAL_STRING("PKPRootNotBuiltIn");break;default:consoleErrorTag=NS_LITERAL_STRING("PKPUnknownError");break;}}/** * Process a single security header. Only two types are supported: HSTS and HPKP. */nsresultnsHttpChannel::ProcessSingleSecurityHeader(uint32_taType,nsISSLStatus*aSSLStatus,uint32_taFlags){nsHttpAtomatom;switch(aType){casensISiteSecurityService::HEADER_HSTS:atom=nsHttp::ResolveAtom("Strict-Transport-Security");break;casensISiteSecurityService::HEADER_HPKP:atom=nsHttp::ResolveAtom("Public-Key-Pins");break;default:NS_NOTREACHED("Invalid security header type");returnNS_ERROR_FAILURE;}nsAutoCStringsecurityHeader;nsresultrv=mResponseHead->GetHeader(atom,securityHeader);if(NS_SUCCEEDED(rv)){nsISiteSecurityService*sss=gHttpHandler->GetSSService();NS_ENSURE_TRUE(sss,NS_ERROR_OUT_OF_MEMORY);// Process header will now discard the headers itself if the channel// wasn't secure (whereas before it had to be checked manually)OriginAttributesoriginAttributes;NS_GetOriginAttributes(this,originAttributes);uint32_tfailureResult;uint32_theaderSource=nsISiteSecurityService::SOURCE_ORGANIC_REQUEST;if(mLoadInfo&&mLoadInfo->GetIsHSTSPriming()){headerSource=nsISiteSecurityService::SOURCE_HSTS_PRIMING;}rv=sss->ProcessHeader(aType,mURI,securityHeader,aSSLStatus,aFlags,headerSource,originAttributes,nullptr,nullptr,&failureResult);if(NS_FAILED(rv)){nsAutoStringconsoleErrorCategory;nsAutoStringconsoleErrorTag;switch(aType){casensISiteSecurityService::HEADER_HSTS:GetSTSConsoleErrorTag(failureResult,consoleErrorTag);consoleErrorCategory=NS_LITERAL_STRING("Invalid HSTS Headers");break;casensISiteSecurityService::HEADER_HPKP:GetPKPConsoleErrorTag(failureResult,consoleErrorTag);consoleErrorCategory=NS_LITERAL_STRING("Invalid HPKP Headers");break;default:returnNS_ERROR_FAILURE;}Unused<<AddSecurityMessage(consoleErrorTag,consoleErrorCategory);LOG(("nsHttpChannel: Failed to parse %s header, continuing load.\n",atom.get()));}}else{if(rv!=NS_ERROR_NOT_AVAILABLE){// All other errors are fatalNS_ENSURE_SUCCESS(rv,rv);}LOG(("nsHttpChannel: No %s header, continuing load.\n",atom.get()));}returnNS_OK;}/** * Decide whether or not to remember Strict-Transport-Security, and whether * or not to enforce channel integrity. * * @return NS_ERROR_FAILURE if there's security information missing even though * it's an HTTPS connection. */nsresultnsHttpChannel::ProcessSecurityHeaders(){nsresultrv;boolisHttps=false;rv=mURI->SchemeIs("https",&isHttps);NS_ENSURE_SUCCESS(rv,rv);// If this channel is not loading securely, STS or PKP doesn't do anything.// In the case of HSTS, the upgrade to HTTPS takes place earlier in the// channel load process.if(!isHttps)returnNS_OK;nsAutoCStringasciiHost;rv=mURI->GetAsciiHost(asciiHost);NS_ENSURE_SUCCESS(rv,NS_OK);// If the channel is not a hostname, but rather an IP, do not process STS// or PKP headersPRNetAddrhostAddr;if(PR_SUCCESS==PR_StringToNetAddr(asciiHost.get(),&hostAddr))returnNS_OK;// mSecurityInfo may not always be present, and if it's not then it is okay// to just disregard any security headers since we know nothing about the// security of the connection.NS_ENSURE_TRUE(mSecurityInfo,NS_OK);uint32_tflags=NS_UsePrivateBrowsing(this)?nsISocketProvider::NO_PERMANENT_STORAGE:0;// Get the SSLStatusnsCOMPtr<nsISSLStatusProvider>sslprov=do_QueryInterface(mSecurityInfo);NS_ENSURE_TRUE(sslprov,NS_ERROR_FAILURE);nsCOMPtr<nsISSLStatus>sslStatus;rv=sslprov->GetSSLStatus(getter_AddRefs(sslStatus));NS_ENSURE_SUCCESS(rv,rv);NS_ENSURE_TRUE(sslStatus,NS_ERROR_FAILURE);rv=ProcessSingleSecurityHeader(nsISiteSecurityService::HEADER_HSTS,sslStatus,flags);NS_ENSURE_SUCCESS(rv,rv);rv=ProcessSingleSecurityHeader(nsISiteSecurityService::HEADER_HPKP,sslStatus,flags);NS_ENSURE_SUCCESS(rv,rv);returnNS_OK;}nsresultnsHttpChannel::ProcessContentSignatureHeader(nsHttpResponseHead*aResponseHead){nsresultrv=NS_OK;// we only do this if we require it in loadInfoif(!mLoadInfo||!mLoadInfo->GetVerifySignedContent()){returnNS_OK;}NS_ENSURE_TRUE(aResponseHead,NS_ERROR_ABORT);nsAutoCStringcontentSignatureHeader;nsHttpAtomatom=nsHttp::ResolveAtom("Content-Signature");rv=aResponseHead->GetHeader(atom,contentSignatureHeader);if(NS_FAILED(rv)){LOG(("Content-Signature header is missing but expected."));DoInvalidateCacheEntry(mURI);returnNS_ERROR_INVALID_SIGNATURE;}// if we require a signature but it is empty, failif(contentSignatureHeader.IsEmpty()){DoInvalidateCacheEntry(mURI);LOG(("An expected content-signature header is missing.\n"));returnNS_ERROR_INVALID_SIGNATURE;}// we ensure a content type here to avoid running into problems with// content sniffing, which might sniff parts of the content before we can// verify the signatureif(!aResponseHead->HasContentType()){NS_WARNING("Empty content type can get us in trouble when verifying ""content signatures");returnNS_ERROR_INVALID_SIGNATURE;}// create a new listener that meadiates the contentRefPtr<ContentVerifier>contentVerifyingMediator=newContentVerifier(mListener,mListenerContext);rv=contentVerifyingMediator->Init(contentSignatureHeader,this,mListenerContext);NS_ENSURE_SUCCESS(rv,NS_ERROR_INVALID_SIGNATURE);mListener=contentVerifyingMediator;returnNS_OK;}/** * Decide whether or not to send a security report and, if so, give the * SecurityReporter the information required to send such a report. */voidnsHttpChannel::ProcessSecurityReport(nsresultstatus){uint32_terrorClass;nsCOMPtr<nsINSSErrorsService>errSvc=do_GetService("@mozilla.org/nss_errors_service;1");// getErrorClass will throw a generic NS_ERROR_FAILURE if the error code is// not in the set of errors covered by the NSS errors service.nsresultrv=errSvc->GetErrorClass(status,&errorClass);if(!NS_SUCCEEDED(rv)){return;}// if the content was not loaded succesfully and we have security info,// send a TLS error report - we must do this early as other parts of// OnStopRequest can return earlyboolreportingEnabled=Preferences::GetBool("security.ssl.errorReporting.enabled");boolreportingAutomatic=Preferences::GetBool("security.ssl.errorReporting.automatic");if(!mSecurityInfo||!reportingEnabled||!reportingAutomatic){return;}nsCOMPtr<nsITransportSecurityInfo>secInfo=do_QueryInterface(mSecurityInfo);nsCOMPtr<nsISecurityReporter>errorReporter=do_GetService("@mozilla.org/securityreporter;1");if(!secInfo||!mURI){return;}nsAutoCStringhostStr;int32_tport;rv=mURI->GetHost(hostStr);if(!NS_SUCCEEDED(rv)){return;}rv=mURI->GetPort(&port);if(NS_SUCCEEDED(rv)){errorReporter->ReportTLSError(secInfo,hostStr,port);}}boolnsHttpChannel::IsHTTPS(){boolisHttps;if(NS_FAILED(mURI->SchemeIs("https",&isHttps))||!isHttps)returnfalse;returntrue;}voidnsHttpChannel::ProcessSSLInformation(){// If this is HTTPS, record any use of RSA so that Key Exchange Algorithm// can be whitelisted for TLS False Start in future sessions. We could// do the same for DH but its rarity doesn't justify the lookup.if(mCanceled||NS_FAILED(mStatus)||!mSecurityInfo||!IsHTTPS()||mPrivateBrowsing)return;nsCOMPtr<nsISSLStatusProvider>statusProvider=do_QueryInterface(mSecurityInfo);if(!statusProvider)return;nsCOMPtr<nsISSLStatus>sslstat;statusProvider->GetSSLStatus(getter_AddRefs(sslstat));if(!sslstat)return;nsCOMPtr<nsITransportSecurityInfo>securityInfo=do_QueryInterface(mSecurityInfo);uint32_tstate;if(securityInfo&&NS_SUCCEEDED(securityInfo->GetSecurityState(&state))&&(state&nsIWebProgressListener::STATE_IS_BROKEN)){// Send weak crypto warnings to the web consoleif(state&nsIWebProgressListener::STATE_USES_WEAK_CRYPTO){nsStringconsoleErrorTag=NS_LITERAL_STRING("WeakCipherSuiteWarning");nsStringconsoleErrorCategory=NS_LITERAL_STRING("SSL");Unused<<AddSecurityMessage(consoleErrorTag,consoleErrorCategory);}}// Send (SHA-1) signature algorithm errors to the web consolensCOMPtr<nsIX509Cert>cert;sslstat->GetServerCert(getter_AddRefs(cert));if(cert){UniqueCERTCertificatenssCert(cert->GetCert());if(nssCert){SECOidTagtag=SECOID_GetAlgorithmTag(&nssCert->signature);LOG(("Checking certificate signature: The OID tag is %i [this=%p]\n",tag,this));// Check to see if the signature is sha-1 based.// Not including checks for SEC_OID_ISO_SHA1_WITH_RSA_SIGNATURE// from http://tools.ietf.org/html/rfc2437#section-8 since I// can't see reference to it outside this specif(tag==SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION||tag==SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST||tag==SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE){nsStringconsoleErrorTag=NS_LITERAL_STRING("SHA1Sig");nsStringconsoleErrorMessage=NS_LITERAL_STRING("SHA-1 Signature");Unused<<AddSecurityMessage(consoleErrorTag,consoleErrorMessage);}}}}voidnsHttpChannel::ProcessAltService(){// e.g. Alt-Svc: h2=":443"; ma=60// e.g. Alt-Svc: h2="otherhost:443"// Alt-Svc = 1#( alternative *( OWS ";" OWS parameter ) )// alternative = protocol-id "=" alt-authority// protocol-id = token ; percent-encoded ALPN protocol identifier// alt-authority = quoted-string ; containing [ uri-host ] ":" portif(!mAllowAltSvc){// per channel opt outreturn;}if(!gHttpHandler->AllowAltSvc()||(mCaps&NS_HTTP_DISALLOW_SPDY)){return;}nsAutoCStringscheme;mURI->GetScheme(scheme);boolisHttp=scheme.Equals(NS_LITERAL_CSTRING("http"));if(!isHttp&&!scheme.Equals(NS_LITERAL_CSTRING("https"))){return;}nsAutoCStringaltSvc;Unused<<mResponseHead->GetHeader(nsHttp::Alternate_Service,altSvc);if(altSvc.IsEmpty()){return;}if(!nsHttp::IsReasonableHeaderValue(altSvc)){LOG(("Alt-Svc Response Header seems unreasonable - skipping\n"));return;}nsAutoCStringoriginHost;int32_toriginPort=80;mURI->GetPort(&originPort);if(NS_FAILED(mURI->GetHost(originHost))){return;}nsCOMPtr<nsIInterfaceRequestor>callbacks;nsCOMPtr<nsProxyInfo>proxyInfo;NS_NewNotificationCallbacksAggregation(mCallbacks,mLoadGroup,getter_AddRefs(callbacks));if(mProxyInfo){proxyInfo=do_QueryInterface(mProxyInfo);}OriginAttributesoriginAttributes;NS_GetOriginAttributes(this,originAttributes);AltSvcMapping::ProcessHeader(altSvc,scheme,originHost,originPort,mUsername,mPrivateBrowsing,callbacks,proxyInfo,mCaps&NS_HTTP_DISALLOW_SPDY,originAttributes);}nsresultnsHttpChannel::ProcessResponse(){uint32_thttpStatus=mResponseHead->Status();LOG(("nsHttpChannel::ProcessResponse [this=%p httpStatus=%u]\n",this,httpStatus));// do some telemetryif(gHttpHandler->IsTelemetryEnabled()){// Gather data on whether the transaction and page (if this is// the initial page load) is being loaded with SSL.Telemetry::Accumulate(Telemetry::HTTP_TRANSACTION_IS_SSL,mConnectionInfo->EndToEndSSL());if(mLoadFlags&LOAD_INITIAL_DOCUMENT_URI){Telemetry::Accumulate(Telemetry::HTTP_PAGELOAD_IS_SSL,mConnectionInfo->EndToEndSSL());}// how often do we see something like Alt-Svc: "443:quic,p=1"nsAutoCStringalt_service;Unused<<mResponseHead->GetHeader(nsHttp::Alternate_Service,alt_service);boolsaw_quic=(!alt_service.IsEmpty()&&PL_strstr(alt_service.get(),"quic"))?1:0;Telemetry::Accumulate(Telemetry::HTTP_SAW_QUIC_ALT_PROTOCOL,saw_quic);// Gather data on how many URLS get redirectedswitch(httpStatus){case200:Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE,0);break;case301:Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE,1);break;case302:Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE,2);break;case304:Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE,3);break;case307:Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE,4);break;case308:Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE,5);break;case400:Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE,6);break;case401:Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE,7);break;case403:Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE,8);break;case404:Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE,9);break;case500:Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE,10);break;default:Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE,11);break;}}// Let the predictor know whether this was a cacheable response or not so// that it knows whether or not to possibly prefetch this resource in the// future.// We use GetReferringPage because mReferrer may not be set at all, or may// not be a full URI (HttpBaseChannel::SetReferrer has the gorey details).// If that's null, though, we'll fall back to mReferrer just in case (this// is especially useful in xpcshell tests, where we don't have an actual// pageload to get a referrer from).nsCOMPtr<nsIURI>referrer=GetReferringPage();if(!referrer){referrer=mReferrer;}if(referrer){nsCOMPtr<nsILoadContextInfo>lci=GetLoadContextInfo(this);mozilla::net::Predictor::UpdateCacheability(referrer,mURI,httpStatus,mRequestHead,mResponseHead,lci);}if(mTransaction->ProxyConnectFailed()){// Only allow 407 (authentication required) to continueif(httpStatus!=407)returnProcessFailedProxyConnect(httpStatus);// If proxy CONNECT response needs to complete, wait to process connection// for Strict-Transport-Security.}else{// Given a successful connection, process any STS or PKP data that's// relevant.DebugOnly<nsresult>rv=ProcessSecurityHeaders();MOZ_ASSERT(NS_SUCCEEDED(rv),"ProcessSTSHeader failed, continuing load.");}MOZ_ASSERT(!mCachedContentIsValid||mRaceCacheWithNetwork,"We should not be hitting the network if we have valid cached ""content unless we are racing the network and cache");ProcessSSLInformation();// notify "http-on-examine-response" observersgHttpHandler->OnExamineResponse(this);returnContinueProcessResponse1();}voidnsHttpChannel::AsyncContinueProcessResponse(){nsresultrv;rv=ContinueProcessResponse1();if(NS_FAILED(rv)){// A synchronous failure here would normally be passed as the return// value from OnStartRequest, which would in turn cancel the request.// If we're continuing asynchronously, we need to cancel the request// ourselves.Unused<<Cancel(rv);}}nsresultnsHttpChannel::ContinueProcessResponse1(){NS_PRECONDITION(!mCallOnResume,"How did that happen?");nsresultrv;if(mSuspendCount){LOG(("Waiting until resume to finish processing response [this=%p]\n",this));mCallOnResume=&nsHttpChannel::AsyncContinueProcessResponse;returnNS_OK;}// Check if request was cancelled during http-on-examine-response.if(mCanceled){returnCallOnStartRequest();}uint32_thttpStatus=mResponseHead->Status();// Cookies and Alt-Service should not be handled on proxy failure either.// This would be consolidated with ProcessSecurityHeaders but it should// happen after OnExamineResponse.if(!mTransaction->ProxyConnectFailed()&&(httpStatus!=407)){nsAutoCStringcookie;if(NS_SUCCEEDED(mResponseHead->GetHeader(nsHttp::Set_Cookie,cookie))){SetCookie(cookie.get());}if((httpStatus<500)&&(httpStatus!=421)){ProcessAltService();}}if(mConcurrentCacheAccess&&mCachedContentIsPartial&&httpStatus!=206){LOG((" only expecting 206 when doing partial request during ""interrupted cache concurrent read"));returnNS_ERROR_CORRUPTED_CONTENT;}// handle unused username and password in url (see bug 232567)if(httpStatus!=401&&httpStatus!=407){if(!mAuthRetryPending){rv=mAuthProvider->CheckForSuperfluousAuth();if(NS_FAILED(rv)){LOG((" CheckForSuperfluousAuth failed (%08x)",static_cast<uint32_t>(rv)));}}if(mCanceled)returnCallOnStartRequest();// reset the authentication's current continuation state because our// last authentication attempt has been completed successfullyrv=mAuthProvider->Disconnect(NS_ERROR_ABORT);if(NS_FAILED(rv)){LOG((" Disconnect failed (%08x)",static_cast<uint32_t>(rv)));}mAuthProvider=nullptr;LOG((" continuation state has been reset"));}if(mAPIRedirectToURI&&!mCanceled){MOZ_ASSERT(!mOnStartRequestCalled);nsCOMPtr<nsIURI>redirectTo;mAPIRedirectToURI.swap(redirectTo);PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse2);rv=StartRedirectChannelToURI(redirectTo,nsIChannelEventSink::REDIRECT_TEMPORARY);if(NS_SUCCEEDED(rv)){returnNS_OK;}PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse2);}// Hack: ContinueProcessResponse2 uses NS_OK to detect successful// redirects, so we distinguish this codepath (a non-redirect that's// processing normally) by passing in a bogus error code.returnContinueProcessResponse2(NS_BINDING_FAILED);}nsresultnsHttpChannel::ContinueProcessResponse2(nsresultrv){LOG(("nsHttpChannel::ContinueProcessResponse1 [this=%p, rv=%"PRIx32"]",this,static_cast<uint32_t>(rv)));if(NS_SUCCEEDED(rv)){// redirectTo() has passed through, we don't want to go on with// this channel. It will now be canceled by the redirect handling// code that called this function.returnNS_OK;}rv=NS_OK;uint32_thttpStatus=mResponseHead->Status();boolsuccessfulReval=false;// handle different server response categories. Note that we handle// caching or not caching of error pages in// nsHttpResponseHead::MustValidate; if you change this switch, update that// oneswitch(httpStatus){case200:case203:// Per RFC 2616, 14.35.2, "A server MAY ignore the Range header".// So if a server does that and sends 200 instead of 206 that we// expect, notify our caller.// However, if we wanted to start from the beginning, let it go throughif(mResuming&&mStartPos!=0){LOG(("Server ignored our Range header, cancelling [this=%p]\n",this));Cancel(NS_ERROR_NOT_RESUMABLE);rv=CallOnStartRequest();break;}// these can normally be cachedrv=ProcessNormal();MaybeInvalidateCacheEntryForSubsequentGet();break;case206:if(mCachedContentIsPartial)// an internal byte range request...rv=ProcessPartialContent();else{mCacheInputStream.CloseAndRelease();rv=ProcessNormal();}break;case300:case301:case302:case307:case308:case303:#if 0 case 305: // disabled as a security measure (see bug 187996).#endif// don't store the response body for redirectsMaybeInvalidateCacheEntryForSubsequentGet();PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse3);rv=AsyncProcessRedirection(httpStatus);if(NS_FAILED(rv)){PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse3);LOG(("AsyncProcessRedirection failed [rv=%"PRIx32"]\n",static_cast<uint32_t>(rv)));// don't cache failed redirect responses.if(mCacheEntry)mCacheEntry->AsyncDoom(nullptr);if(DoNotRender3xxBody(rv)){mStatus=rv;DoNotifyListener();}else{rv=ContinueProcessResponse3(rv);}}break;case304:if(!ShouldBypassProcessNotModified()){rv=ProcessNotModified();if(NS_SUCCEEDED(rv)){successfulReval=true;break;}LOG(("ProcessNotModified failed [rv=%"PRIx32"]\n",static_cast<uint32_t>(rv)));// We cannot read from the cache entry, it might be in an// incosistent state. Doom it and redirect the channel// to the same URI to reload from the network.mCacheInputStream.CloseAndRelease();if(mCacheEntry){mCacheEntry->AsyncDoom(nullptr);mCacheEntry=nullptr;}rv=StartRedirectChannelToURI(mURI,nsIChannelEventSink::REDIRECT_INTERNAL);if(NS_SUCCEEDED(rv)){returnNS_OK;}}// Don't cache uninformative 304if(mCustomConditionalRequest){CloseCacheEntry(false);}if(ShouldBypassProcessNotModified()||NS_FAILED(rv)){rv=ProcessNormal();}break;case401:case407:if(MOZ_UNLIKELY(mCustomAuthHeader)&&httpStatus==401){// When a custom auth header fails, we don't want to try// any cached credentials, nor we want to ask the user.// It's up to the consumer to re-try w/o setting a custom// auth header if cached credentials should be attempted.rv=NS_ERROR_FAILURE;}else{rv=mAuthProvider->ProcessAuthentication(httpStatus,mConnectionInfo->EndToEndSSL()&&mTransaction->ProxyConnectFailed());}if(rv==NS_ERROR_IN_PROGRESS){// authentication prompt has been invoked and result// is expected asynchronouslymAuthRetryPending=true;if(httpStatus==407||mTransaction->ProxyConnectFailed())mProxyAuthPending=true;// suspend the transaction pump to stop receiving the// unauthenticated content data. We will throw that data// away when user provides credentials or resume the pump// when user refuses to authenticate.LOG(("Suspending the transaction, asynchronously prompting for credentials"));mTransactionPump->Suspend();rv=NS_OK;}elseif(NS_FAILED(rv)){LOG(("ProcessAuthentication failed [rv=%"PRIx32"]\n",static_cast<uint32_t>(rv)));if(mTransaction->ProxyConnectFailed())returnProcessFailedProxyConnect(httpStatus);if(!mAuthRetryPending){rv=mAuthProvider->CheckForSuperfluousAuth();if(NS_FAILED(rv)){LOG(("CheckForSuperfluousAuth failed [rv=%x]\n",static_cast<uint32_t>(rv)));}}rv=ProcessNormal();}else{mAuthRetryPending=true;// see DoAuthRetry}break;default:rv=ProcessNormal();MaybeInvalidateCacheEntryForSubsequentGet();break;}if(gHttpHandler->IsTelemetryEnabled()){CacheDispositioncacheDisposition;if(!mDidReval){cacheDisposition=kCacheMissed;}elseif(successfulReval){cacheDisposition=kCacheHitViaReval;}else{cacheDisposition=kCacheMissedViaReval;}AccumulateCacheHitTelemetry(cacheDisposition);Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_VERSION,mResponseHead->Version());if(mResponseHead->Version()==NS_HTTP_VERSION_0_9){// DefaultPortTopLevel = 0, DefaultPortSubResource = 1,// NonDefaultPortTopLevel = 2, NonDefaultPortSubResource = 3uint32_tv09Info=0;if(!(mLoadFlags&LOAD_INITIAL_DOCUMENT_URI)){v09Info+=1;}if(mConnectionInfo->OriginPort()!=mConnectionInfo->DefaultPort()){v09Info+=2;}Telemetry::Accumulate(Telemetry::HTTP_09_INFO,v09Info);}}returnrv;}nsresultnsHttpChannel::ContinueProcessResponse3(nsresultrv){booldoNotRender=DoNotRender3xxBody(rv);if(rv==NS_ERROR_DOM_BAD_URI&&mRedirectURI){boolisHTTP=false;if(NS_FAILED(mRedirectURI->SchemeIs("http",&isHTTP)))isHTTP=false;if(!isHTTP&&NS_FAILED(mRedirectURI->SchemeIs("https",&isHTTP)))isHTTP=false;if(!isHTTP){// This was a blocked attempt to redirect and subvert the system by// redirecting to another protocol (perhaps javascript:)// In that case we want to throw an error instead of displaying the// non-redirected response body.LOG(("ContinueProcessResponse3 detected rejected Non-HTTP Redirection"));doNotRender=true;rv=NS_ERROR_CORRUPTED_CONTENT;}}if(doNotRender){Cancel(rv);DoNotifyListener();returnrv;}if(NS_SUCCEEDED(rv)){UpdateInhibitPersistentCachingFlag();rv=InitCacheEntry();if(NS_FAILED(rv)){LOG(("ContinueProcessResponse3 ""failed to init cache entry [rv=%x]\n",static_cast<uint32_t>(rv)));}CloseCacheEntry(false);if(mApplicationCacheForWrite){// Store response in the offline cacheUnused<<InitOfflineCacheEntry();CloseOfflineCacheEntry();}returnNS_OK;}LOG(("ContinueProcessResponse3 got failure result [rv=%"PRIx32"]\n",static_cast<uint32_t>(rv)));if(mTransaction&&mTransaction->ProxyConnectFailed()){returnProcessFailedProxyConnect(mRedirectType);}returnProcessNormal();}nsresultnsHttpChannel::ProcessNormal(){nsresultrv;LOG(("nsHttpChannel::ProcessNormal [this=%p]\n",this));boolsucceeded;rv=GetRequestSucceeded(&succeeded);if(NS_SUCCEEDED(rv)&&!succeeded){PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessNormal);boolwaitingForRedirectCallback;Unused<<ProcessFallback(&waitingForRedirectCallback);if(waitingForRedirectCallback){// The transaction has been suspended by ProcessFallback.returnNS_OK;}PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessNormal);}returnContinueProcessNormal(NS_OK);}nsresultnsHttpChannel::ContinueProcessNormal(nsresultrv){LOG(("nsHttpChannel::ContinueProcessNormal [this=%p]",this));if(NS_FAILED(rv)){// Fill the failure status here, we have failed to fall back, thus we// have to report our status as failed.mStatus=rv;DoNotifyListener();returnrv;}if(mFallingBack){// Do not continue with normal processing, fallback is in// progress now.returnNS_OK;}// if we're here, then any byte-range requests failed to result in a partial// response. we must clear this flag to prevent BufferPartialContent from// being called inside our OnDataAvailable (see bug 136678).mCachedContentIsPartial=false;ClearBogusContentEncodingIfNeeded();UpdateInhibitPersistentCachingFlag();// this must be called before firing OnStartRequest, since http clients,// such as imagelib, expect our cache entry to already have the correct// expiration time (bug 87710).if(mCacheEntry){rv=InitCacheEntry();if(NS_FAILED(rv))CloseCacheEntry(true);}// Check that the server sent us what we were asking forif(mResuming){// Create an entity id from the responsensAutoCStringid;rv=GetEntityID(id);if(NS_FAILED(rv)){// If creating an entity id is not possible -> errorCancel(NS_ERROR_NOT_RESUMABLE);}elseif(mResponseHead->Status()!=206&&mResponseHead->Status()!=200){// Probably 404 Not Found, 412 Precondition Failed or// 416 Invalid Range -> errorLOG(("Unexpected response status while resuming, aborting [this=%p]\n",this));Cancel(NS_ERROR_ENTITY_CHANGED);}// If we were passed an entity id, verify it's equal to the server'selseif(!mEntityID.IsEmpty()){if(!mEntityID.Equals(id)){LOG(("Entity mismatch, expected '%s', got '%s', aborting [this=%p]",mEntityID.get(),id.get(),this));Cancel(NS_ERROR_ENTITY_CHANGED);}}}rv=CallOnStartRequest();if(NS_FAILED(rv))returnrv;// install cache listener if we still have a cache entry openif(mCacheEntry&&!mCacheEntryIsReadOnly){rv=InstallCacheListener();if(NS_FAILED(rv))returnrv;}returnNS_OK;}nsresultnsHttpChannel::PromptTempRedirect(){if(!gHttpHandler->PromptTempRedirect()){returnNS_OK;}nsresultrv;nsCOMPtr<nsIStringBundleService>bundleService=do_GetService(NS_STRINGBUNDLE_CONTRACTID,&rv);if(NS_FAILED(rv))returnrv;nsCOMPtr<nsIStringBundle>stringBundle;rv=bundleService->CreateBundle(NECKO_MSGS_URL,getter_AddRefs(stringBundle));if(NS_FAILED(rv))returnrv;nsXPIDLStringmessageString;rv=stringBundle->GetStringFromName(u"RepostFormData",getter_Copies(messageString));// GetStringFromName can return NS_OK and nullptr messageString.if(NS_SUCCEEDED(rv)&&messageString){boolrepost=false;nsCOMPtr<nsIPrompt>prompt;GetCallback(prompt);if(!prompt)returnNS_ERROR_NO_INTERFACE;prompt->Confirm(nullptr,messageString,&repost);if(!repost)returnNS_ERROR_FAILURE;}returnrv;}nsresultnsHttpChannel::ProxyFailover(){LOG(("nsHttpChannel::ProxyFailover [this=%p]\n",this));nsresultrv;nsCOMPtr<nsIProtocolProxyService>pps=do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID,&rv);if(NS_FAILED(rv))returnrv;nsCOMPtr<nsIProxyInfo>pi;rv=pps->GetFailoverForProxy(mConnectionInfo->ProxyInfo(),mURI,mStatus,getter_AddRefs(pi));if(NS_FAILED(rv))returnrv;// XXXbz so where does this codepath remove us from the loadgroup,// exactly?returnAsyncDoReplaceWithProxy(pi);}voidnsHttpChannel::HandleAsyncRedirectChannelToHttps(){NS_PRECONDITION(!mCallOnResume,"How did that happen?");if(mSuspendCount){LOG(("Waiting until resume to do async redirect to https [this=%p]\n",this));mCallOnResume=&nsHttpChannel::HandleAsyncRedirectChannelToHttps;return;}nsresultrv=StartRedirectChannelToHttps();if(NS_FAILED(rv)){rv=ContinueAsyncRedirectChannelToURI(rv);if(NS_FAILED(rv)){LOG(("ContinueAsyncRedirectChannelToURI failed (%08x) [this=%p]\n",static_cast<uint32_t>(rv),this));}}}nsresultnsHttpChannel::StartRedirectChannelToHttps(){LOG(("nsHttpChannel::HandleAsyncRedirectChannelToHttps() [STS]\n"));nsCOMPtr<nsIURI>upgradedURI;nsresultrv=NS_GetSecureUpgradedURI(mURI,getter_AddRefs(upgradedURI));NS_ENSURE_SUCCESS(rv,rv);returnStartRedirectChannelToURI(upgradedURI,nsIChannelEventSink::REDIRECT_PERMANENT|nsIChannelEventSink::REDIRECT_STS_UPGRADE);}voidnsHttpChannel::HandleAsyncAPIRedirect(){NS_PRECONDITION(!mCallOnResume,"How did that happen?");NS_PRECONDITION(mAPIRedirectToURI,"How did that happen?");if(mSuspendCount){LOG(("Waiting until resume to do async API redirect [this=%p]\n",this));mCallOnResume=&nsHttpChannel::HandleAsyncAPIRedirect;return;}nsresultrv=StartRedirectChannelToURI(mAPIRedirectToURI,nsIChannelEventSink::REDIRECT_PERMANENT);if(NS_FAILED(rv)){rv=ContinueAsyncRedirectChannelToURI(rv);if(NS_FAILED(rv)){LOG(("ContinueAsyncRedirectChannelToURI failed (%08x) [this=%p]\n",static_cast<uint32_t>(rv),this));}}return;}nsresultnsHttpChannel::StartRedirectChannelToURI(nsIURI*upgradedURI,uint32_tflags){nsresultrv=NS_OK;LOG(("nsHttpChannel::StartRedirectChannelToURI()\n"));nsCOMPtr<nsIChannel>newChannel;nsCOMPtr<nsILoadInfo>redirectLoadInfo=CloneLoadInfoForRedirect(upgradedURI,flags);nsCOMPtr<nsIIOService>ioService;rv=gHttpHandler->GetIOService(getter_AddRefs(ioService));NS_ENSURE_SUCCESS(rv,rv);rv=NS_NewChannelInternal(getter_AddRefs(newChannel),upgradedURI,redirectLoadInfo,nullptr,// aLoadGroupnullptr,// aCallbacksnsIRequest::LOAD_NORMAL,ioService);NS_ENSURE_SUCCESS(rv,rv);rv=SetupReplacementChannel(upgradedURI,newChannel,true,flags);NS_ENSURE_SUCCESS(rv,rv);// Inform consumers about this fake redirectmRedirectChannel=newChannel;if(!(flags&nsIChannelEventSink::REDIRECT_STS_UPGRADE)&&mInterceptCache==INTERCEPTED){// Mark the channel as intercepted in order to propagate the response URL.nsCOMPtr<nsIHttpChannelInternal>httpRedirect=do_QueryInterface(mRedirectChannel);if(httpRedirect){rv=httpRedirect->ForceIntercepted(mInterceptionID);MOZ_ASSERT(NS_SUCCEEDED(rv));}}PushRedirectAsyncFunc(&nsHttpChannel::ContinueAsyncRedirectChannelToURI);rv=gHttpHandler->AsyncOnChannelRedirect(this,newChannel,flags);if(NS_SUCCEEDED(rv))rv=WaitForRedirectCallback();if(NS_FAILED(rv)){AutoRedirectVetoNotifiernotifier(this);/* Remove the async call to ContinueAsyncRedirectChannelToURI(). * It is called directly by our callers upon return (to clean up * the failed redirect). */PopRedirectAsyncFunc(&nsHttpChannel::ContinueAsyncRedirectChannelToURI);}returnrv;}nsresultnsHttpChannel::ContinueAsyncRedirectChannelToURI(nsresultrv){LOG(("nsHttpChannel::ContinueAsyncRedirectChannelToURI [this=%p]",this));// Since we handle mAPIRedirectToURI also after on-examine-response handler// rather drop it here to avoid any redirect loops, even just hypothetical.mAPIRedirectToURI=nullptr;if(NS_SUCCEEDED(rv)){rv=OpenRedirectChannel(rv);}if(NS_FAILED(rv)){// Cancel the channel here, the update to https had been vetoed// but from the security reasons we have to discard the whole channel// load.Cancel(rv);}if(mLoadGroup){mLoadGroup->RemoveRequest(this,nullptr,mStatus);}if(NS_FAILED(rv)&&!mCachePump&&!mTransactionPump){// We have to manually notify the listener because there is not any pump// that would call our OnStart/StopRequest after resume from waiting for// the redirect callback.DoNotifyListener();}returnrv;}nsresultnsHttpChannel::OpenRedirectChannel(nsresultrv){AutoRedirectVetoNotifiernotifier(this);// Make sure to do this after we received redirect veto answer,// i.e. after all sinks had been notifiedmRedirectChannel->SetOriginalURI(mOriginalURI);// And now, notify observers the deprecated waynsCOMPtr<nsIHttpEventSink>httpEventSink;GetCallback(httpEventSink);if(httpEventSink){// NOTE: nsIHttpEventSink is only used for compatibility with pre-1.8// versions.rv=httpEventSink->OnRedirect(this,mRedirectChannel);if(NS_FAILED(rv)){returnrv;}}// open new channelif(mLoadInfo&&mLoadInfo->GetEnforceSecurity()){MOZ_ASSERT(!mListenerContext,"mListenerContext should be null!");rv=mRedirectChannel->AsyncOpen2(mListener);}else{rv=mRedirectChannel->AsyncOpen(mListener,mListenerContext);}NS_ENSURE_SUCCESS(rv,rv);mStatus=NS_BINDING_REDIRECTED;notifier.RedirectSucceeded();ReleaseListeners();returnNS_OK;}nsresultnsHttpChannel::AsyncDoReplaceWithProxy(nsIProxyInfo*pi){LOG(("nsHttpChannel::AsyncDoReplaceWithProxy [this=%p pi=%p]",this,pi));nsresultrv;nsCOMPtr<nsIChannel>newChannel;rv=gHttpHandler->NewProxiedChannel2(mURI,pi,mProxyResolveFlags,mProxyURI,mLoadInfo,getter_AddRefs(newChannel));if(NS_FAILED(rv))returnrv;uint32_tflags=nsIChannelEventSink::REDIRECT_INTERNAL;rv=SetupReplacementChannel(mURI,newChannel,true,flags);if(NS_FAILED(rv))returnrv;// Inform consumers about this fake redirectmRedirectChannel=newChannel;PushRedirectAsyncFunc(&nsHttpChannel::ContinueDoReplaceWithProxy);rv=gHttpHandler->AsyncOnChannelRedirect(this,newChannel,flags);if(NS_SUCCEEDED(rv))rv=WaitForRedirectCallback();if(NS_FAILED(rv)){AutoRedirectVetoNotifiernotifier(this);PopRedirectAsyncFunc(&nsHttpChannel::ContinueDoReplaceWithProxy);}returnrv;}nsresultnsHttpChannel::ContinueDoReplaceWithProxy(nsresultrv){AutoRedirectVetoNotifiernotifier(this);if(NS_FAILED(rv))returnrv;NS_PRECONDITION(mRedirectChannel,"No redirect channel?");// Make sure to do this after we received redirect veto answer,// i.e. after all sinks had been notifiedmRedirectChannel->SetOriginalURI(mOriginalURI);// open new channelif(mLoadInfo&&mLoadInfo->GetEnforceSecurity()){MOZ_ASSERT(!mListenerContext,"mListenerContext should be null!");rv=mRedirectChannel->AsyncOpen2(mListener);}else{rv=mRedirectChannel->AsyncOpen(mListener,mListenerContext);}NS_ENSURE_SUCCESS(rv,rv);mStatus=NS_BINDING_REDIRECTED;notifier.RedirectSucceeded();ReleaseListeners();returnrv;}nsresultnsHttpChannel::ResolveProxy(){LOG(("nsHttpChannel::ResolveProxy [this=%p]\n",this));nsresultrv;nsCOMPtr<nsIProtocolProxyService>pps=do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID,&rv);if(NS_FAILED(rv))returnrv;// using the nsIProtocolProxyService2 allows a minor performance// optimization, but if an add-on has only provided the original interface// then it is ok to use that version.nsCOMPtr<nsIProtocolProxyService2>pps2=do_QueryInterface(pps);if(pps2){rv=pps2->AsyncResolve2(this,mProxyResolveFlags,this,getter_AddRefs(mProxyRequest));}else{rv=pps->AsyncResolve(static_cast<nsIChannel*>(this),mProxyResolveFlags,this,getter_AddRefs(mProxyRequest));}returnrv;}boolnsHttpChannel::ResponseWouldVary(nsICacheEntry*entry){nsresultrv;nsAutoCStringbuf,metaKey;Unused<<mCachedResponseHead->GetHeader(nsHttp::Vary,buf);if(!buf.IsEmpty()){NS_NAMED_LITERAL_CSTRING(prefix,"request-");// enumerate the elements of the Vary header...char*val=buf.BeginWriting();// going to munge bufchar*token=nsCRT::strtok(val,NS_HTTP_HEADER_SEPS,&val);while(token){LOG(("nsHttpChannel::ResponseWouldVary [channel=%p] " \"processing %s\n",this,token));//// if "*", then assume response would vary. technically speaking,// "Vary: header, *" is not permitted, but we allow it anyways.//// We hash values of cookie-headers for the following reasons://// 1- cookies can be very large in size//// 2- cookies may contain sensitive information. (for parity with// out policy of not storing Set-cookie headers in the cache// meta data, we likewise do not want to store cookie headers// here.)//if(*token=='*')returntrue;// if we encounter this, just get out of here// build cache meta data key...metaKey=prefix+nsDependentCString(token);// check the last value of the given request header to see if it has// since changed. if so, then indeed the cached response is invalid.nsXPIDLCStringlastVal;entry->GetMetaDataElement(metaKey.get(),getter_Copies(lastVal));LOG(("nsHttpChannel::ResponseWouldVary [channel=%p] ""stored value = \"%s\"\n",this,lastVal.get()));// Look for value of "Cookie" in the request headersnsHttpAtomatom=nsHttp::ResolveAtom(token);nsAutoCStringnewVal;boolhasHeader=NS_SUCCEEDED(mRequestHead.GetHeader(atom,newVal));if(!lastVal.IsEmpty()){// value for this header in cache, but no value in requestif(!hasHeader){returntrue;// yes - response would vary}// If this is a cookie-header, stored metadata is not// the value itself but the hash. So we also hash the// outgoing value here in order to compare the hashesnsAutoCStringhash;if(atom==nsHttp::Cookie){rv=Hash(newVal.get(),hash);// If hash failed, be conservative (the cached hash// exists at this point) and claim response would varyif(NS_FAILED(rv))returntrue;newVal=hash;LOG(("nsHttpChannel::ResponseWouldVary [this=%p] " \"set-cookie value hashed to %s\n",this,newVal.get()));}if(!newVal.Equals(lastVal)){returntrue;// yes, response would vary}}elseif(hasHeader){// old value is empty, but newVal is setreturntrue;}// next token...token=nsCRT::strtok(val,NS_HTTP_HEADER_SEPS,&val);}}returnfalse;}// We need to have an implementation of this function just so that we can keep// all references to mCallOnResume of type nsHttpChannel: it's not OK in C++// to set a member function ptr to a base class function.voidnsHttpChannel::HandleAsyncAbort(){HttpAsyncAborter<nsHttpChannel>::HandleAsyncAbort();}//-----------------------------------------------------------------------------// nsHttpChannel <byte-range>//-----------------------------------------------------------------------------boolnsHttpChannel::IsResumable(int64_tpartialLen,int64_tcontentLength,boolignoreMissingPartialLen)const{boolhasContentEncoding=mCachedResponseHead->HasHeader(nsHttp::Content_Encoding);nsAutoCStringetag;Unused<<mCachedResponseHead->GetHeader(nsHttp::ETag,etag);boolhasWeakEtag=!etag.IsEmpty()&&StringBeginsWith(etag,NS_LITERAL_CSTRING("W/"));return(partialLen<contentLength)&&(partialLen>0||ignoreMissingPartialLen)&&!hasContentEncoding&&!hasWeakEtag&&mCachedResponseHead->IsResumable()&&!mCustomConditionalRequest&&!mCachedResponseHead->NoStore();}nsresultnsHttpChannel::MaybeSetupByteRangeRequest(int64_tpartialLen,int64_tcontentLength,boolignoreMissingPartialLen){// Be pesimisticmIsPartialRequest=false;if(!IsResumable(partialLen,contentLength,ignoreMissingPartialLen))returnNS_ERROR_NOT_RESUMABLE;// looks like a partial entry we can reuse; add If-Range// and Range headers.nsresultrv=SetupByteRangeRequest(partialLen);if(NS_FAILED(rv)){// Make the request unconditional again.UntieByteRangeRequest();}returnrv;}nsresultnsHttpChannel::SetupByteRangeRequest(int64_tpartialLen){// cached content has been found to be partial, add necessary request// headers to complete cache entry.// use strongest validator available...nsAutoCStringval;Unused<<mCachedResponseHead->GetHeader(nsHttp::ETag,val);if(val.IsEmpty())Unused<<mCachedResponseHead->GetHeader(nsHttp::Last_Modified,val);if(val.IsEmpty()){// if we hit this code it means mCachedResponseHead->IsResumable() is// either broken or not being called.NS_NOTREACHED("no cache validator");mIsPartialRequest=false;returnNS_ERROR_FAILURE;}charbuf[64];SprintfLiteral(buf,"bytes=%"PRId64"-",partialLen);DebugOnly<nsresult>rv;rv=mRequestHead.SetHeader(nsHttp::Range,nsDependentCString(buf));MOZ_ASSERT(NS_SUCCEEDED(rv));rv=mRequestHead.SetHeader(nsHttp::If_Range,val);MOZ_ASSERT(NS_SUCCEEDED(rv));mIsPartialRequest=true;returnNS_OK;}voidnsHttpChannel::UntieByteRangeRequest(){DebugOnly<nsresult>rv;rv=mRequestHead.ClearHeader(nsHttp::Range);MOZ_ASSERT(NS_SUCCEEDED(rv));rv=mRequestHead.ClearHeader(nsHttp::If_Range);MOZ_ASSERT(NS_SUCCEEDED(rv));}nsresultnsHttpChannel::ProcessPartialContent(){// ok, we've just received a 206//// we need to stream whatever data is in the cache out first, and then// pick up whatever data is on the wire, writing it into the cache.LOG(("nsHttpChannel::ProcessPartialContent [this=%p]\n",this));NS_ENSURE_TRUE(mCachedResponseHead,NS_ERROR_NOT_INITIALIZED);NS_ENSURE_TRUE(mCacheEntry,NS_ERROR_NOT_INITIALIZED);// Make sure to clear bogus content-encodings before looking at the headerClearBogusContentEncodingIfNeeded();// Check if the content-encoding we now got is different from the one we// got beforensAutoCStringcontentEncoding,cachedContentEncoding;// It is possible that there is not such headersUnused<<mResponseHead->GetHeader(nsHttp::Content_Encoding,contentEncoding);Unused<<mCachedResponseHead->GetHeader(nsHttp::Content_Encoding,cachedContentEncoding);if(PL_strcasecmp(contentEncoding.get(),cachedContentEncoding.get())!=0){Cancel(NS_ERROR_INVALID_CONTENT_ENCODING);returnCallOnStartRequest();}nsresultrv;int64_tcachedContentLength=mCachedResponseHead->ContentLength();int64_tentitySize=mResponseHead->TotalEntitySize();nsAutoCStringcontentRange;Unused<<mResponseHead->GetHeader(nsHttp::Content_Range,contentRange);LOG(("nsHttpChannel::ProcessPartialContent [this=%p trans=%p] ""original content-length %"PRId64", entity-size %"PRId64", content-range %s\n",this,mTransaction.get(),cachedContentLength,entitySize,contentRange.get()));if((entitySize>=0)&&(cachedContentLength>=0)&&(entitySize!=cachedContentLength)){LOG(("nsHttpChannel::ProcessPartialContent [this=%p] ""206 has different total entity size than the content length ""of the original partially cached entity.\n",this));mCacheEntry->AsyncDoom(nullptr);Cancel(NS_ERROR_CORRUPTED_CONTENT);returnCallOnStartRequest();}if(mConcurrentCacheAccess){// We started to read cached data sooner than its write has been done.// But the concurrent write has not finished completely, so we had to// do a range request. Now let the content coming from the network// be presented to consumers and also stored to the cache entry.rv=InstallCacheListener(mLogicalOffset);if(NS_FAILED(rv))returnrv;if(mOfflineCacheEntry){rv=InstallOfflineCacheListener(mLogicalOffset);if(NS_FAILED(rv))returnrv;}}else{// suspend the current transactionrv=mTransactionPump->Suspend();if(NS_FAILED(rv))returnrv;}// merge any new headers with the cached response headersrv=mCachedResponseHead->UpdateHeaders(mResponseHead);if(NS_FAILED(rv))returnrv;// update the cached response headnsAutoCStringhead;mCachedResponseHead->Flatten(head,true);rv=mCacheEntry->SetMetaDataElement("response-head",head.get());if(NS_FAILED(rv))returnrv;// make the cached response be the current responsemResponseHead=Move(mCachedResponseHead);UpdateInhibitPersistentCachingFlag();rv=UpdateExpirationTime();if(NS_FAILED(rv))returnrv;// notify observers interested in looking at a response that has been// merged with any cached headers (http-on-examine-merged-response).gHttpHandler->OnExamineMergedResponse(this);if(mConcurrentCacheAccess){mCachedContentIsPartial=false;// Leave the mConcurrentCacheAccess flag set, we want to use it// to prevent duplicate OnStartRequest call on the target listener// in case this channel is canceled before it gets its OnStartRequest// from the http transaction.// Now we continue reading the network response.}else{// the cached content is valid, although incomplete.mCachedContentIsValid=true;rv=ReadFromCache(false);}returnrv;}nsresultnsHttpChannel::OnDoneReadingPartialCacheEntry(bool*streamDone){nsresultrv;LOG(("nsHttpChannel::OnDoneReadingPartialCacheEntry [this=%p]",this));// by default, assume we would have streamed all data or failed...*streamDone=true;// setup cache listener to append to cache entryint64_tsize;rv=mCacheEntry->GetDataSize(&size);if(NS_FAILED(rv))returnrv;rv=InstallCacheListener(size);if(NS_FAILED(rv))returnrv;// Entry is valid, do it now, after the output stream has been opened,// otherwise when done earlier, pending readers would consider the cache// entry still as partial (CacheEntry::GetDataSize would return the partial// data size) and consumers would do the conditional request again.rv=mCacheEntry->SetValid();if(NS_FAILED(rv))returnrv;// need to track the logical offset of the data being sent to our listenermLogicalOffset=size;// we're now completing the cached content, so we can clear this flag.// this puts us in the state of a regular download.mCachedContentIsPartial=false;// The cache input stream pump is finished, we do not need it any more.// (see bug 1313923)mCachePump=nullptr;// resume the transaction if it exists, otherwise the pipe contained the// remaining part of the document and we've now streamed all of the data.if(mTransactionPump){rv=mTransactionPump->Resume();if(NS_SUCCEEDED(rv))*streamDone=false;}elseNS_NOTREACHED("no transaction");returnrv;}//-----------------------------------------------------------------------------// nsHttpChannel <cache>//-----------------------------------------------------------------------------boolnsHttpChannel::ShouldBypassProcessNotModified(){if(mCustomConditionalRequest){LOG(("Bypassing ProcessNotModified due to custom conditional headers"));returntrue;}if(!mDidReval){LOG(("Server returned a 304 response even though we did not send a ""conditional request"));returntrue;}returnfalse;}nsresultnsHttpChannel::ProcessNotModified(){nsresultrv;LOG(("nsHttpChannel::ProcessNotModified [this=%p]\n",this));// Assert ShouldBypassProcessNotModified() has been checked before call to// ProcessNotModified().MOZ_ASSERT(!ShouldBypassProcessNotModified());MOZ_ASSERT(mCachedResponseHead);MOZ_ASSERT(mCacheEntry);NS_ENSURE_TRUE(mCachedResponseHead&&mCacheEntry,NS_ERROR_UNEXPECTED);// If the 304 response contains a Last-Modified different than the// one in our cache that is pretty suspicious and is, in at least the// case of bug 716840, a sign of the server having previously corrupted// our cache with a bad response. Take the minor step here of just dooming// that cache entry so there is a fighting chance of getting things on the// right track.nsAutoCStringlastModifiedCached;nsAutoCStringlastModified304;rv=mCachedResponseHead->GetHeader(nsHttp::Last_Modified,lastModifiedCached);if(NS_SUCCEEDED(rv)){rv=mResponseHead->GetHeader(nsHttp::Last_Modified,lastModified304);}if(NS_SUCCEEDED(rv)&&!lastModified304.Equals(lastModifiedCached)){LOG(("Cache Entry and 304 Last-Modified Headers Do Not Match ""[%s] and [%s]\n",lastModifiedCached.get(),lastModified304.get()));mCacheEntry->AsyncDoom(nullptr);Telemetry::Accumulate(Telemetry::CACHE_LM_INCONSISTENT,true);}// merge any new headers with the cached response headersrv=mCachedResponseHead->UpdateHeaders(mResponseHead);if(NS_FAILED(rv))returnrv;// update the cached response headnsAutoCStringhead;mCachedResponseHead->Flatten(head,true);rv=mCacheEntry->SetMetaDataElement("response-head",head.get());if(NS_FAILED(rv))returnrv;// make the cached response be the current responsemResponseHead=Move(mCachedResponseHead);UpdateInhibitPersistentCachingFlag();rv=UpdateExpirationTime();if(NS_FAILED(rv))returnrv;rv=AddCacheEntryHeaders(mCacheEntry);if(NS_FAILED(rv))returnrv;// notify observers interested in looking at a reponse that has been// merged with any cached headersgHttpHandler->OnExamineMergedResponse(this);mCachedContentIsValid=true;// Tell other consumers the entry is OK to userv=mCacheEntry->SetValid();if(NS_FAILED(rv))returnrv;rv=ReadFromCache(false);if(NS_FAILED(rv))returnrv;mTransactionReplaced=true;returnNS_OK;}nsresultnsHttpChannel::ProcessFallback(bool*waitingForRedirectCallback){LOG(("nsHttpChannel::ProcessFallback [this=%p]\n",this));nsresultrv;*waitingForRedirectCallback=false;mFallingBack=false;// At this point a load has failed (either due to network problems// or an error returned on the server). Perform an application// cache fallback if we have a URI to fall back to.if(!mApplicationCache||mFallbackKey.IsEmpty()||mFallbackChannel){LOG((" choosing not to fallback [%p,%s,%d]",mApplicationCache.get(),mFallbackKey.get(),mFallbackChannel));returnNS_OK;}// Make sure the fallback entry hasn't been marked as a foreign// entry.uint32_tfallbackEntryType;rv=mApplicationCache->GetTypes(mFallbackKey,&fallbackEntryType);NS_ENSURE_SUCCESS(rv,rv);if(fallbackEntryType&nsIApplicationCache::ITEM_FOREIGN){// This cache points to a fallback that refers to a different// manifest. Refuse to fall back.returnNS_OK;}MOZ_ASSERT(fallbackEntryType&nsIApplicationCache::ITEM_FALLBACK,"Fallback entry not marked correctly!");// Kill any offline cache entry, and disable offline caching for the// fallback.if(mOfflineCacheEntry){mOfflineCacheEntry->AsyncDoom(nullptr);mOfflineCacheEntry=nullptr;}mApplicationCacheForWrite=nullptr;mOfflineCacheEntry=nullptr;// Close the current cache entry.CloseCacheEntry(true);// Create a new channel to load the fallback entry.RefPtr<nsIChannel>newChannel;rv=gHttpHandler->NewChannel2(mURI,mLoadInfo,getter_AddRefs(newChannel));NS_ENSURE_SUCCESS(rv,rv);uint32_tredirectFlags=nsIChannelEventSink::REDIRECT_INTERNAL;rv=SetupReplacementChannel(mURI,newChannel,true,redirectFlags);NS_ENSURE_SUCCESS(rv,rv);// Make sure the new channel loads from the fallback key.nsCOMPtr<nsIHttpChannelInternal>httpInternal=do_QueryInterface(newChannel,&rv);NS_ENSURE_SUCCESS(rv,rv);rv=httpInternal->SetupFallbackChannel(mFallbackKey.get());NS_ENSURE_SUCCESS(rv,rv);// ... and fallbacks should only load from the cache.uint32_tnewLoadFlags=mLoadFlags|LOAD_REPLACE|LOAD_ONLY_FROM_CACHE;rv=newChannel->SetLoadFlags(newLoadFlags);// Inform consumers about this fake redirectmRedirectChannel=newChannel;PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessFallback);rv=gHttpHandler->AsyncOnChannelRedirect(this,newChannel,redirectFlags);if(NS_SUCCEEDED(rv))rv=WaitForRedirectCallback();if(NS_FAILED(rv)){AutoRedirectVetoNotifiernotifier(this);PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessFallback);returnrv;}// Indicate we are now waiting for the asynchronous redirect callback// if all went OK.*waitingForRedirectCallback=true;returnNS_OK;}nsresultnsHttpChannel::ContinueProcessFallback(nsresultrv){AutoRedirectVetoNotifiernotifier(this);if(NS_FAILED(rv))returnrv;NS_PRECONDITION(mRedirectChannel,"No redirect channel?");// Make sure to do this after we received redirect veto answer,// i.e. after all sinks had been notifiedmRedirectChannel->SetOriginalURI(mOriginalURI);if(mLoadInfo&&mLoadInfo->GetEnforceSecurity()){MOZ_ASSERT(!mListenerContext,"mListenerContext should be null!");rv=mRedirectChannel->AsyncOpen2(mListener);}else{rv=mRedirectChannel->AsyncOpen(mListener,mListenerContext);}NS_ENSURE_SUCCESS(rv,rv);if(mLoadFlags&LOAD_INITIAL_DOCUMENT_URI){MaybeWarnAboutAppCache();}// close down this channelCancel(NS_BINDING_REDIRECTED);notifier.RedirectSucceeded();ReleaseListeners();mFallingBack=true;returnNS_OK;}// Determines if a request is a byte range request for a subrange,// i.e. is a byte range request, but not a 0- byte range request.staticboolIsSubRangeRequest(nsHttpRequestHead&aRequestHead){nsAutoCStringbyteRange;if(NS_FAILED(aRequestHead.GetHeader(nsHttp::Range,byteRange))){returnfalse;}return!byteRange.EqualsLiteral("bytes=0-");}nsresultnsHttpChannel::OpenCacheEntry(boolisHttps){// Handle correctly mCacheEntriesToWaitForAutoCacheWaitFlagswaitFlags(this);// Drop this flag heremConcurrentCacheAccess=0;nsresultrv;mLoadedFromApplicationCache=false;mHasQueryString=HasQueryString(mRequestHead.ParsedMethod(),mURI);LOG(("nsHttpChannel::OpenCacheEntry [this=%p]",this));// make sure we're not abusing this functionNS_PRECONDITION(!mCacheEntry,"cache entry already open");nsAutoCStringcacheKey;nsAutoCStringextension;if(mRequestHead.IsPost()){// If the post id is already set then this is an attempt to replay// a post transaction via the cache. Otherwise, we need a unique// post id for this transaction.if(mPostID==0)mPostID=gHttpHandler->GenerateUniqueID();}elseif(!PossiblyIntercepted()&&!mRequestHead.IsGet()&&!mRequestHead.IsHead()){// don't use the cache for other types of requestsreturnNS_OK;}if(mResuming){// We don't support caching for requests initiated// via nsIResumableChannel.returnNS_OK;}// Don't cache byte range requests which are subranges, only cache 0-// byte range requests.if(IsSubRangeRequest(mRequestHead))returnNS_OK;// Pick up an application cache from the notification// callbacks if available and if we are not an intercepted channel.if(!PossiblyIntercepted()&&!mApplicationCache&&mInheritApplicationCache){nsCOMPtr<nsIApplicationCacheContainer>appCacheContainer;GetCallback(appCacheContainer);if(appCacheContainer){appCacheContainer->GetApplicationCache(getter_AddRefs(mApplicationCache));}}nsCOMPtr<nsICacheStorageService>cacheStorageService(services::GetCacheStorageService());if(!cacheStorageService){returnNS_ERROR_NOT_AVAILABLE;}nsCOMPtr<nsICacheStorage>cacheStorage;nsCOMPtr<nsIURI>openURI;if(!mFallbackKey.IsEmpty()&&mFallbackChannel){// This is a fallback channel, open fallback URI insteadrv=NS_NewURI(getter_AddRefs(openURI),mFallbackKey);NS_ENSURE_SUCCESS(rv,rv);}else{// In the case of intercepted channels, we need to construct the cache// entry key based on the original URI, so that in case the intercepted// channel is redirected, the cache entry key before and after the// redirect is the same.if(PossiblyIntercepted()){openURI=mOriginalURI;}else{openURI=mURI;}}RefPtr<LoadContextInfo>info=GetLoadContextInfo(this);if(!info){returnNS_ERROR_FAILURE;}uint32_tcacheEntryOpenFlags;booloffline=gIOService->IsOffline();nsAutoCStringcacheControlRequestHeader;Unused<<mRequestHead.GetHeader(nsHttp::Cache_Control,cacheControlRequestHeader);CacheControlParsercacheControlRequest(cacheControlRequestHeader);if(cacheControlRequest.NoStore()&&!PossiblyIntercepted()){gotobypassCacheEntryOpen;}if(offline||(mLoadFlags&INHIBIT_CACHING)){if(BYPASS_LOCAL_CACHE(mLoadFlags)&&!offline&&!PossiblyIntercepted()){gotobypassCacheEntryOpen;}cacheEntryOpenFlags=nsICacheStorage::OPEN_READONLY;mCacheEntryIsReadOnly=true;}elseif(BYPASS_LOCAL_CACHE(mLoadFlags)&&!mApplicationCache){cacheEntryOpenFlags=nsICacheStorage::OPEN_TRUNCATE;}else{cacheEntryOpenFlags=nsICacheStorage::OPEN_NORMALLY|nsICacheStorage::CHECK_MULTITHREADED;}// Remember the request is a custom conditional request so that we can// process any 304 response correctly.mCustomConditionalRequest=mRequestHead.HasHeader(nsHttp::If_Modified_Since)||mRequestHead.HasHeader(nsHttp::If_None_Match)||mRequestHead.HasHeader(nsHttp::If_Unmodified_Since)||mRequestHead.HasHeader(nsHttp::If_Match)||mRequestHead.HasHeader(nsHttp::If_Range);if(!mPostID&&mApplicationCache){rv=cacheStorageService->AppCacheStorage(info,mApplicationCache,getter_AddRefs(cacheStorage));}elseif(PossiblyIntercepted()){// The synthesized cache has less restrictions on file size and so on.rv=cacheStorageService->SynthesizedCacheStorage(info,getter_AddRefs(cacheStorage));}elseif(mLoadFlags&INHIBIT_PERSISTENT_CACHING){rv=cacheStorageService->MemoryCacheStorage(info,// ? choose app cache as well...getter_AddRefs(cacheStorage));}elseif(mPinCacheContent){rv=cacheStorageService->PinningCacheStorage(info,getter_AddRefs(cacheStorage));}else{rv=cacheStorageService->DiskCacheStorage(info,!mPostID&&(mChooseApplicationCache||(mLoadFlags&LOAD_CHECK_OFFLINE_CACHE)),getter_AddRefs(cacheStorage));}NS_ENSURE_SUCCESS(rv,rv);if((mClassOfService&nsIClassOfService::Leader)||(mLoadFlags&LOAD_INITIAL_DOCUMENT_URI))cacheEntryOpenFlags|=nsICacheStorage::OPEN_PRIORITY;// Only for backward compatibility with the old cache back end.// When removed, remove the flags and related code snippets.if(mLoadFlags&LOAD_BYPASS_LOCAL_CACHE_IF_BUSY)cacheEntryOpenFlags|=nsICacheStorage::OPEN_BYPASS_IF_BUSY;if(PossiblyIntercepted()){extension.Append(nsPrintfCString("u%"PRIu64,mInterceptionID));}elseif(mPostID){extension.Append(nsPrintfCString("%d",mPostID));}// If this channel should be intercepted, we do not open a cache entry for this channel// until the interception process is complete and the consumer decides what to do with it.if(mInterceptCache==MAYBE_INTERCEPT){DebugOnly<bool>exists;MOZ_ASSERT(NS_FAILED(cacheStorage->Exists(openURI,extension,&exists))||!exists,"The entry must not exist in the cache before we create it here");nsCOMPtr<nsICacheEntry>entry;rv=cacheStorage->OpenTruncate(openURI,extension,getter_AddRefs(entry));NS_ENSURE_SUCCESS(rv,rv);nsCOMPtr<nsINetworkInterceptController>controller;GetCallback(controller);RefPtr<InterceptedChannelChrome>intercepted=newInterceptedChannelChrome(this,controller,entry);intercepted->NotifyController();}else{if(mInterceptCache==INTERCEPTED){cacheEntryOpenFlags|=nsICacheStorage::OPEN_INTERCEPTED;// Clear OPEN_TRUNCATE for the fake cache entry, since otherwise// cache storage will close the current entry which breaks the// response synthesis.cacheEntryOpenFlags&=~nsICacheStorage::OPEN_TRUNCATE;DebugOnly<bool>exists;MOZ_ASSERT(NS_SUCCEEDED(cacheStorage->Exists(openURI,extension,&exists))&&exists,"The entry must exist in the cache after we create it here");}mCacheOpenWithPriority=cacheEntryOpenFlags&nsICacheStorage::OPEN_PRIORITY;mCacheQueueSizeWhenOpen=CacheStorageService::CacheQueueSize(mCacheOpenWithPriority);boolhasAltData=false;uint32_tsizeInKb=0;rv=cacheStorage->GetCacheIndexEntryAttrs(openURI,extension,&hasAltData,&sizeInKb);// We will attempt to race the network vs the cache if we've found this// entry in the cache index, and it has appropriate// attributes (doesn't have alt-data, and has a small size)if(sRCWNEnabled&&mInterceptCache!=INTERCEPTED&&NS_SUCCEEDED(rv)&&!hasAltData&&sizeInKb<sRCWNSmallResourceSizeKB){MaybeRaceCacheWithNetwork();}if(!mCacheOpenDelay){MOZ_ASSERT(NS_IsMainThread(),"Should be called on the main thread");mCacheAsyncOpenCalled=true;if(mNetworkTriggered){mRaceCacheWithNetwork=true;}rv=cacheStorage->AsyncOpenURI(openURI,extension,cacheEntryOpenFlags,this);}else{// We pass `this` explicitly as a parameter due to the raw pointer// to refcounted object in lambda analysis.mCacheOpenFunc=[openURI,extension,cacheEntryOpenFlags,cacheStorage](nsHttpChannel*self)->void{MOZ_ASSERT(NS_IsMainThread(),"Should be called on the main thread");self->mCacheAsyncOpenCalled=true;if(self->mNetworkTriggered){self->mRaceCacheWithNetwork=true;}cacheStorage->AsyncOpenURI(openURI,extension,cacheEntryOpenFlags,self);};mCacheOpenTimer=do_CreateInstance(NS_TIMER_CONTRACTID);// calls nsHttpChannel::Notify after `mCacheOpenDelay` millisecondsmCacheOpenTimer->InitWithCallback(this,mCacheOpenDelay,nsITimer::TYPE_ONE_SHOT);}NS_ENSURE_SUCCESS(rv,rv);}waitFlags.Keep(WAIT_FOR_CACHE_ENTRY);bypassCacheEntryOpen:if(!mApplicationCacheForWrite)returnNS_OK;// If there is an app cache to write to, open the entry right now in parallel.// make sure we're not abusing this functionNS_PRECONDITION(!mOfflineCacheEntry,"cache entry already open");if(offline){// only put things in the offline cache while onlinereturnNS_OK;}if(mLoadFlags&INHIBIT_CACHING){// respect demand not to cachereturnNS_OK;}if(!mRequestHead.IsGet()){// only cache complete documents offlinereturnNS_OK;}rv=cacheStorageService->AppCacheStorage(info,mApplicationCacheForWrite,getter_AddRefs(cacheStorage));NS_ENSURE_SUCCESS(rv,rv);rv=cacheStorage->AsyncOpenURI(mURI,EmptyCString(),nsICacheStorage::OPEN_TRUNCATE,this);NS_ENSURE_SUCCESS(rv,rv);waitFlags.Keep(WAIT_FOR_OFFLINE_CACHE_ENTRY);returnNS_OK;}nsresultnsHttpChannel::CheckPartial(nsICacheEntry*aEntry,int64_t*aSize,int64_t*aContentLength){nsresultrv;rv=aEntry->GetDataSize(aSize);if(NS_ERROR_IN_PROGRESS==rv){*aSize=-1;rv=NS_OK;}NS_ENSURE_SUCCESS(rv,rv);nsHttpResponseHead*responseHead=mCachedResponseHead?mCachedResponseHead:mResponseHead;if(!responseHead)returnNS_ERROR_UNEXPECTED;*aContentLength=responseHead->ContentLength();returnNS_OK;}voidnsHttpChannel::UntieValidationRequest(){DebugOnly<nsresult>rv;// Make the request unconditional again.rv=mRequestHead.ClearHeader(nsHttp::If_Modified_Since);MOZ_ASSERT(NS_SUCCEEDED(rv));rv=mRequestHead.ClearHeader(nsHttp::If_None_Match);MOZ_ASSERT(NS_SUCCEEDED(rv));rv=mRequestHead.ClearHeader(nsHttp::ETag);MOZ_ASSERT(NS_SUCCEEDED(rv));}NS_IMETHODIMPnsHttpChannel::OnCacheEntryCheck(nsICacheEntry*entry,nsIApplicationCache*appCache,uint32_t*aResult){nsresultrv=NS_OK;LOG(("nsHttpChannel::OnCacheEntryCheck enter [channel=%p entry=%p]",this,entry));if(mRaceCacheWithNetwork&&mFirstResponseSource==RESPONSE_FROM_NETWORK){LOG(("Not using cached response because we've already got one from the network\n"));*aResult=ENTRY_NOT_WANTED;// Net-win indicates that mOnStartRequestTimestamp is from net.int64_tsavedTime=(TimeStamp::Now()-mOnStartRequestTimestamp).ToMilliseconds();Telemetry::Accumulate(Telemetry::NETWORK_RACE_CACHE_WITH_NETWORK_SAVED_TIME,savedTime);returnNS_OK;}elseif(mRaceCacheWithNetwork&&mFirstResponseSource==RESPONSE_PENDING){mOnCacheEntryCheckTimestamp=TimeStamp::Now();}nsAutoCStringcacheControlRequestHeader;Unused<<mRequestHead.GetHeader(nsHttp::Cache_Control,cacheControlRequestHeader);CacheControlParsercacheControlRequest(cacheControlRequestHeader);if(cacheControlRequest.NoStore()){LOG(("Not using cached response based on no-store request cache directive\n"));*aResult=ENTRY_NOT_WANTED;returnNS_OK;}// Be pessimistic: assume the cache entry has no useful data.*aResult=ENTRY_WANTED;mCachedContentIsValid=false;nsXPIDLCStringbuf;// Get the method that was used to generate the cached responserv=entry->GetMetaDataElement("request-method",getter_Copies(buf));NS_ENSURE_SUCCESS(rv,rv);boolmethodWasHead=buf.EqualsLiteral("HEAD");boolmethodWasGet=buf.EqualsLiteral("GET");if(methodWasHead){// The cached response does not contain an entity. We can only reuse// the response if the current request is also HEAD.if(!mRequestHead.IsHead()){returnNS_OK;}}buf.Adopt(0);// We'll need this value in later computations...uint32_tlastModifiedTime;rv=entry->GetLastModified(&lastModifiedTime);NS_ENSURE_SUCCESS(rv,rv);// Determine if this is the first time that this cache entry// has been accessed during this session.boolfromPreviousSession=(gHttpHandler->SessionStartTime()>lastModifiedTime);// Get the cached HTTP response headersmCachedResponseHead=newnsHttpResponseHead();// A "original-response-headers" metadata element holds network original headers,// i.e. the headers in the form as they arrieved from the network.// We need to get the network original headers first, because we need to keep them// in order.rv=entry->GetMetaDataElement("original-response-headers",getter_Copies(buf));if(NS_SUCCEEDED(rv)){rv=mCachedResponseHead->ParseCachedOriginalHeaders((char*)buf.get());if(NS_FAILED(rv)){LOG((" failed to parse original-response-headers\n"));}}buf.Adopt(0);// A "response-head" metadata element holds response head, e.g. response status// line and headers in the form Firefox uses them internally (no dupicate// headers, etc.).rv=entry->GetMetaDataElement("response-head",getter_Copies(buf));NS_ENSURE_SUCCESS(rv,rv);// Parse string stored in a "response-head" metadata element.// These response headers will be merged with the orignal headers (i.e. the// headers stored in a "original-response-headers" metadata element).rv=mCachedResponseHead->ParseCachedHead(buf.get());NS_ENSURE_SUCCESS(rv,rv);buf.Adopt(0);boolisCachedRedirect=WillRedirect(mCachedResponseHead);// Do not return 304 responses from the cache, and also do not return// any other non-redirect 3xx responses from the cache (see bug 759043).NS_ENSURE_TRUE((mCachedResponseHead->Status()/100!=3)||isCachedRedirect,NS_ERROR_ABORT);if(mCachedResponseHead->NoStore()&&mCacheEntryIsReadOnly){// This prevents loading no-store responses when navigating back// while the browser is set to work offline.LOG((" entry loading as read-only but is no-store, set INHIBIT_CACHING"));mLoadFlags|=nsIRequest::INHIBIT_CACHING;}// Don't bother to validate items that are read-only,// unless they are read-only because of INHIBIT_CACHING or because// we're updating the offline cache.// Don't bother to validate if this is a fallback entry.if(!mApplicationCacheForWrite&&(appCache||(mCacheEntryIsReadOnly&&!(mLoadFlags&nsIRequest::INHIBIT_CACHING))||mFallbackChannel)){rv=OpenCacheInputStream(entry,true,!!appCache);if(NS_SUCCEEDED(rv)){mCachedContentIsValid=true;entry->MaybeMarkValid();}returnrv;}boolwantCompleteEntry=false;if(!methodWasHead&&!isCachedRedirect){// If the cached content-length is set and it does not match the data// size of the cached content, then the cached response is partial...// either we need to issue a byte range request or we need to refetch// the entire document.//// We exclude redirects from this check because we (usually) strip the// entity when we store the cache entry, and even if we didn't, we// always ignore a cached redirect's entity anyway. See bug 759043.int64_tsize,contentLength;rv=CheckPartial(entry,&size,&contentLength);NS_ENSURE_SUCCESS(rv,rv);if(size==int64_t(-1)){LOG((" write is in progress"));if(mLoadFlags&LOAD_BYPASS_LOCAL_CACHE_IF_BUSY){LOG((" not interested in the entry, ""LOAD_BYPASS_LOCAL_CACHE_IF_BUSY specified"));*aResult=ENTRY_NOT_WANTED;returnNS_OK;}// Ignore !(size > 0) from the resumability conditionif(!IsResumable(size,contentLength,true)){LOG((" wait for entry completion, ""response is not resumable"));wantCompleteEntry=true;}else{mConcurrentCacheAccess=1;}}elseif(contentLength!=int64_t(-1)&&contentLength!=size){LOG(("Cached data size does not match the Content-Length header ""[content-length=%"PRId64" size=%"PRId64"]\n",contentLength,size));rv=MaybeSetupByteRangeRequest(size,contentLength);mCachedContentIsPartial=NS_SUCCEEDED(rv)&&mIsPartialRequest;if(mCachedContentIsPartial){rv=OpenCacheInputStream(entry,false,!!appCache);if(NS_FAILED(rv)){UntieByteRangeRequest();returnrv;}*aResult=ENTRY_NEEDS_REVALIDATION;returnNS_OK;}if(size==0&&mCacheOnlyMetadata){// Don't break cache entry load when the entry's data size// is 0 and mCacheOnlyMetadata flag is set. In that case we// want to proceed since the LOAD_ONLY_IF_MODIFIED flag is// also set.MOZ_ASSERT(mLoadFlags&LOAD_ONLY_IF_MODIFIED);}elseif(mInterceptCache!=INTERCEPTED){returnrv;}}}boolisHttps=false;rv=mURI->SchemeIs("https",&isHttps);NS_ENSURE_SUCCESS(rv,rv);booldoValidation=false;boolcanAddImsHeader=true;boolisForcedValid=false;entry->GetIsForcedValid(&isForcedValid);nsXPIDLCStringframedBuf;rv=entry->GetMetaDataElement("strongly-framed",getter_Copies(framedBuf));// describe this in terms of explicitly weakly framed so as to be backwards// compatible with old cache contents which dont have strongly-framed makersboolweaklyFramed=NS_SUCCEEDED(rv)&&framedBuf.EqualsLiteral("0");boolisImmutable=!weaklyFramed&&isHttps&&mCachedResponseHead->Immutable();// Cached entry is not the entity we request (see bug #633743)if(ResponseWouldVary(entry)){LOG(("Validating based on Vary headers returning TRUE\n"));canAddImsHeader=false;doValidation=true;}// Check isForcedValid to see if it is possible to skip validation.// Don't skip validation if we have serious reason to believe that this// content is invalid (it's expired).// See netwerk/cache2/nsICacheEntry.idl for detailselseif(isForcedValid&&(!mCachedResponseHead->ExpiresInPast()||!mCachedResponseHead->MustValidateIfExpired())){LOG(("NOT validating based on isForcedValid being true.\n"));Telemetry::AutoCounter<Telemetry::PREDICTOR_TOTAL_PREFETCHES_USED>used;++used;doValidation=false;}// If the LOAD_FROM_CACHE flag is set, any cached data can simply be usedelseif(mLoadFlags&nsIRequest::LOAD_FROM_CACHE||mAllowStaleCacheContent){LOG(("NOT validating based on LOAD_FROM_CACHE load flag\n"));doValidation=false;}// If the VALIDATE_ALWAYS flag is set, any cached data won't be used until// it's revalidated with the server.elseif((mLoadFlags&nsIRequest::VALIDATE_ALWAYS)&&!isImmutable){LOG(("Validating based on VALIDATE_ALWAYS load flag\n"));doValidation=true;}// Even if the VALIDATE_NEVER flag is set, there are still some cases in// which we must validate the cached response with the server.elseif(mLoadFlags&nsIRequest::VALIDATE_NEVER){LOG(("VALIDATE_NEVER set\n"));// if no-store validate cached response (see bug 112564)if(mCachedResponseHead->NoStore()){LOG(("Validating based on no-store logic\n"));doValidation=true;}else{LOG(("NOT validating based on VALIDATE_NEVER load flag\n"));doValidation=false;}}// check if validation is strictly required...elseif(mCachedResponseHead->MustValidate()){LOG(("Validating based on MustValidate() returning TRUE\n"));doValidation=true;// possibly serve from cache for a custom If-Match/If-Unmodified-Since// conditional request}elseif(mCustomConditionalRequest&&!mRequestHead.HasHeader(nsHttp::If_Match)&&!mRequestHead.HasHeader(nsHttp::If_Unmodified_Since)){LOG(("Validating based on a custom conditional request\n"));doValidation=true;}else{// previously we also checked for a query-url w/out expiration// and didn't do heuristic on it. but defacto that is allowed now.//// Check if the cache entry has expired...uint32_tnow=NowInSeconds();uint32_tage=0;rv=mCachedResponseHead->ComputeCurrentAge(now,now,&age);NS_ENSURE_SUCCESS(rv,rv);uint32_tfreshness=0;rv=mCachedResponseHead->ComputeFreshnessLifetime(&freshness);NS_ENSURE_SUCCESS(rv,rv);uint32_texpiration=0;rv=entry->GetExpirationTime(&expiration);NS_ENSURE_SUCCESS(rv,rv);uint32_tmaxAgeRequest,maxStaleRequest,minFreshRequest;LOG((" NowInSeconds()=%u, expiration time=%u, freshness lifetime=%u, age=%u",now,expiration,freshness,age));if(cacheControlRequest.NoCache()){LOG((" validating, no-cache request"));doValidation=true;}elseif(cacheControlRequest.MaxStale(&maxStaleRequest)){uint32_tstaleTime=age>freshness?age-freshness:0;doValidation=staleTime>maxStaleRequest;LOG((" validating=%d, max-stale=%u requested",doValidation,maxStaleRequest));}elseif(cacheControlRequest.MaxAge(&maxAgeRequest)){doValidation=age>maxAgeRequest;LOG((" validating=%d, max-age=%u requested",doValidation,maxAgeRequest));}elseif(cacheControlRequest.MinFresh(&minFreshRequest)){uint32_tfreshTime=freshness>age?freshness-age:0;doValidation=freshTime<minFreshRequest;LOG((" validating=%d, min-fresh=%u requested",doValidation,minFreshRequest));}elseif(now<=expiration){doValidation=false;LOG((" not validating, expire time not in the past"));}elseif(mCachedResponseHead->MustValidateIfExpired()){doValidation=true;}elseif(mLoadFlags&nsIRequest::VALIDATE_ONCE_PER_SESSION){// If the cached response does not include expiration infor-// mation, then we must validate the response, despite whether// or not this is the first access this session. This behavior// is consistent with existing browsers and is generally expected// by web authors.if(freshness==0)doValidation=true;elsedoValidation=fromPreviousSession;}elsedoValidation=true;LOG(("%salidating based on expiration time\n",doValidation?"V":"Not v"));}// If a content signature is expected to be valid in this load,// set doValidation to force a signature check.if(!doValidation&&mLoadInfo&&mLoadInfo->GetVerifySignedContent()){doValidation=true;}nsAutoCStringrequestedETag;if(!doValidation&&NS_SUCCEEDED(mRequestHead.GetHeader(nsHttp::If_Match,requestedETag))&&(methodWasGet||methodWasHead)){nsAutoCStringcachedETag;Unused<<mCachedResponseHead->GetHeader(nsHttp::ETag,cachedETag);if(!cachedETag.IsEmpty()&&(StringBeginsWith(cachedETag,NS_LITERAL_CSTRING("W/"))||!requestedETag.Equals(cachedETag))){// User has defined If-Match header, if the cached entry is not// matching the provided header value or the cached ETag is weak,// force validation.doValidation=true;}}// Previous error should not be propagated.rv=NS_OK;if(!doValidation){//// Check the authorization headers used to generate the cache entry.// We must validate the cache entry if://// 1) the cache entry was generated prior to this session w/// credentials (see bug 103402).// 2) the cache entry was generated w/o credentials, but would now// require credentials (see bug 96705).//// NOTE: this does not apply to proxy authentication.//entry->GetMetaDataElement("auth",getter_Copies(buf));doValidation=(fromPreviousSession&&!buf.IsEmpty())||(buf.IsEmpty()&&mRequestHead.HasHeader(nsHttp::Authorization));}// Bug #561276: We maintain a chain of cache-keys which returns cached// 3xx-responses (redirects) in order to detect cycles. If a cycle is// found, ignore the cached response and hit the net. Otherwise, use// the cached response and add the cache-key to the chain. Note that// a limited number of redirects (cached or not) is allowed and is// enforced independently of this mechanismif(!doValidation&&isCachedRedirect){nsAutoCStringcacheKey;rv=GenerateCacheKey(mPostID,cacheKey);MOZ_ASSERT(NS_SUCCEEDED(rv));if(!mRedirectedCachekeys)mRedirectedCachekeys=newnsTArray<nsCString>();elseif(mRedirectedCachekeys->Contains(cacheKey))doValidation=true;LOG(("Redirection-chain %s key %s\n",doValidation?"contains":"does not contain",cacheKey.get()));// Append cacheKey if not in the chain alreadyif(!doValidation)mRedirectedCachekeys->AppendElement(cacheKey);}if(doValidation&&mInterceptCache==INTERCEPTED){doValidation=false;}mCachedContentIsValid=!doValidation;if(doValidation){//// now, we are definitely going to issue a HTTP request to the server.// make it conditional if possible.//// do not attempt to validate no-store content, since servers will not// expect it to be cached. (we only keep it in our cache for the// purposes of back/forward, etc.)//// the request method MUST be either GET or HEAD (see bug 175641) and// the cached response code must be < 400//// the cached content must not be weakly framed or marked immutable//// do not override conditional headers when consumer has defined its ownif(!mCachedResponseHead->NoStore()&&(mRequestHead.IsGet()||mRequestHead.IsHead())&&!mCustomConditionalRequest&&!weaklyFramed&&!isImmutable&&(mCachedResponseHead->Status()<400)){if(mConcurrentCacheAccess){// In case of concurrent read and also validation request we// must wait for the current writer to close the output stream// first. Otherwise, when the writer's job would have been interrupted// before all the data were downloaded, we'd have to do a range request// which would be a second request in line during this channel's// life-time. nsHttpChannel is not designed to do that, so rather// turn off concurrent read and wait for entry's completion.// Then only re-validation or range-re-validation request will go out.mConcurrentCacheAccess=0;// This will cause that OnCacheEntryCheck is called again with the same// entry after the writer is done.wantCompleteEntry=true;}else{nsAutoCStringval;// Add If-Modified-Since header if a Last-Modified was given// and we are allowed to do this (see bugs 510359 and 269303)if(canAddImsHeader){Unused<<mCachedResponseHead->GetHeader(nsHttp::Last_Modified,val);if(!val.IsEmpty()){rv=mRequestHead.SetHeader(nsHttp::If_Modified_Since,val);MOZ_ASSERT(NS_SUCCEEDED(rv));}}// Add If-None-Match header if an ETag was given in the responseUnused<<mCachedResponseHead->GetHeader(nsHttp::ETag,val);if(!val.IsEmpty()){rv=mRequestHead.SetHeader(nsHttp::If_None_Match,val);MOZ_ASSERT(NS_SUCCEEDED(rv));}mDidReval=true;}}}if(mCachedContentIsValid||mDidReval){rv=OpenCacheInputStream(entry,mCachedContentIsValid,!!appCache);if(NS_FAILED(rv)){// If we can't get the entity then we have to act as though we// don't have the cache entry.if(mDidReval){UntieValidationRequest();mDidReval=false;}mCachedContentIsValid=false;}}if(mDidReval)*aResult=ENTRY_NEEDS_REVALIDATION;elseif(wantCompleteEntry)*aResult=RECHECK_AFTER_WRITE_FINISHED;else{*aResult=ENTRY_WANTED;}if(mCachedContentIsValid){entry->MaybeMarkValid();}LOG(("nsHTTPChannel::OnCacheEntryCheck exit [this=%p doValidation=%d result=%d]\n",this,doValidation,*aResult));returnrv;}NS_IMETHODIMPnsHttpChannel::OnCacheEntryAvailable(nsICacheEntry*entry,boolaNew,nsIApplicationCache*aAppCache,nsresultstatus){MOZ_ASSERT(NS_IsMainThread());mOnCacheAvailableCalled=true;nsresultrv;LOG(("nsHttpChannel::OnCacheEntryAvailable [this=%p entry=%p ""new=%d appcache=%p status=%"PRIx32" mAppCache=%p mAppCacheForWrite=%p]\n",this,entry,aNew,aAppCache,static_cast<uint32_t>(status),mApplicationCache.get(),mApplicationCacheForWrite.get()));// if the channel's already fired onStopRequest, then we should ignore// this event.if(!mIsPending){mCacheInputStream.CloseAndRelease();returnNS_OK;}rv=OnCacheEntryAvailableInternal(entry,aNew,aAppCache,status);if(NS_FAILED(rv)){CloseCacheEntry(false);Unused<<AsyncAbort(rv);}returnNS_OK;}nsresultnsHttpChannel::OnCacheEntryAvailableInternal(nsICacheEntry*entry,boolaNew,nsIApplicationCache*aAppCache,nsresultstatus){nsresultrv;if(mCanceled){LOG(("channel was canceled [this=%p status=%"PRIx32"]\n",this,static_cast<uint32_t>(static_cast<nsresult>(mStatus))));returnmStatus;}if(aAppCache){if(mApplicationCache==aAppCache&&!mCacheEntry){rv=OnOfflineCacheEntryAvailable(entry,aNew,aAppCache,status);}elseif(mApplicationCacheForWrite==aAppCache&&aNew&&!mOfflineCacheEntry){rv=OnOfflineCacheEntryForWritingAvailable(entry,aAppCache,status);}else{rv=OnOfflineCacheEntryAvailable(entry,aNew,aAppCache,status);}}else{rv=OnNormalCacheEntryAvailable(entry,aNew,status);}if(NS_FAILED(rv)&&(mLoadFlags&LOAD_ONLY_FROM_CACHE)){// If we have a fallback URI (and we're not already// falling back), process the fallback asynchronously.if(!mFallbackChannel&&!mFallbackKey.IsEmpty()){returnAsyncCall(&nsHttpChannel::HandleAsyncFallback);}returnNS_ERROR_DOCUMENT_NOT_CACHED;}if(NS_FAILED(rv)){returnrv;}// We may be waiting for more callbacks...if(AwaitingCacheCallbacks()){returnNS_OK;}if(mCachedContentIsValid&&mNetworkTriggered){Unused<<ReadFromCache(true);}returnTriggerNetwork(0);}nsresultnsHttpChannel::OnNormalCacheEntryAvailable(nsICacheEntry*aEntry,boolaNew,nsresultaEntryStatus){mCacheEntriesToWaitFor&=~WAIT_FOR_CACHE_ENTRY;if(NS_FAILED(aEntryStatus)||aNew){// Make sure this flag is dropped. It may happen the entry is doomed// between OnCacheEntryCheck and OnCacheEntryAvailable.mCachedContentIsValid=false;// From the same reason remove any conditional headers added// in OnCacheEntryCheck.if(mDidReval){LOG((" Removing conditional request headers"));UntieValidationRequest();mDidReval=false;}if(mLoadFlags&LOAD_ONLY_FROM_CACHE){// if this channel is only allowed to pull from the cache, then// we must fail if we were unable to open a cache entry for read.returnNS_ERROR_DOCUMENT_NOT_CACHED;}}if(NS_SUCCEEDED(aEntryStatus)){mCacheEntry=aEntry;mCacheEntryIsWriteOnly=aNew;if(!aNew&&!mAsyncOpenTime.IsNull()){// We use microseconds for IO operations. For consistency let's use// microseconds here too.uint32_tduration=(TimeStamp::Now()-mAsyncOpenTime).ToMicroseconds();boolisSlow=false;if((mCacheOpenWithPriority&&mCacheQueueSizeWhenOpen>=sRCWNQueueSizePriority)||(!mCacheOpenWithPriority&&mCacheQueueSizeWhenOpen>=sRCWNQueueSizeNormal)){isSlow=true;}CacheFileUtils::CachePerfStats::AddValue(CacheFileUtils::CachePerfStats::ENTRY_OPEN,duration,isSlow);}if(mLoadFlags&LOAD_INITIAL_DOCUMENT_URI){Telemetry::Accumulate(Telemetry::HTTP_OFFLINE_CACHE_DOCUMENT_LOAD,false);}}returnNS_OK;}nsresultnsHttpChannel::OnOfflineCacheEntryAvailable(nsICacheEntry*aEntry,boolaNew,nsIApplicationCache*aAppCache,nsresultaEntryStatus){MOZ_ASSERT(!mApplicationCache||aAppCache==mApplicationCache);MOZ_ASSERT(!aNew||!aEntry||mApplicationCacheForWrite);mCacheEntriesToWaitFor&=~WAIT_FOR_CACHE_ENTRY;nsresultrv;if(NS_SUCCEEDED(aEntryStatus)){if(!mApplicationCache){mApplicationCache=aAppCache;}// We successfully opened an offline cache session and the entry,// so indicate we will load from the offline cache.mLoadedFromApplicationCache=true;mCacheEntryIsReadOnly=true;mCacheEntry=aEntry;mCacheEntryIsWriteOnly=false;if(mLoadFlags&LOAD_INITIAL_DOCUMENT_URI&&!mApplicationCacheForWrite){MaybeWarnAboutAppCache();}returnNS_OK;}if(!mApplicationCacheForWrite&&!mFallbackChannel){if(!mApplicationCache){mApplicationCache=aAppCache;}// Check for namespace match.nsCOMPtr<nsIApplicationCacheNamespace>namespaceEntry;rv=mApplicationCache->GetMatchingNamespace(mSpec,getter_AddRefs(namespaceEntry));NS_ENSURE_SUCCESS(rv,rv);uint32_tnamespaceType=0;if(!namespaceEntry||NS_FAILED(namespaceEntry->GetItemType(&namespaceType))||(namespaceType&(nsIApplicationCacheNamespace::NAMESPACE_FALLBACK|nsIApplicationCacheNamespace::NAMESPACE_BYPASS))==0){// When loading from an application cache, only items// on the whitelist or matching a// fallback namespace should hit the network...mLoadFlags|=LOAD_ONLY_FROM_CACHE;// ... and if there were an application cache entry,// we would have found it earlier.returnNS_ERROR_CACHE_KEY_NOT_FOUND;}if(namespaceType&nsIApplicationCacheNamespace::NAMESPACE_FALLBACK){rv=namespaceEntry->GetData(mFallbackKey);NS_ENSURE_SUCCESS(rv,rv);}}returnNS_OK;}nsresultnsHttpChannel::OnOfflineCacheEntryForWritingAvailable(nsICacheEntry*aEntry,nsIApplicationCache*aAppCache,nsresultaEntryStatus){MOZ_ASSERT(mApplicationCacheForWrite&&aAppCache==mApplicationCacheForWrite);mCacheEntriesToWaitFor&=~WAIT_FOR_OFFLINE_CACHE_ENTRY;if(NS_SUCCEEDED(aEntryStatus)){mOfflineCacheEntry=aEntry;if(NS_FAILED(aEntry->GetLastModified(&mOfflineCacheLastModifiedTime))){mOfflineCacheLastModifiedTime=0;}}returnaEntryStatus;}// Generates the proper cache-key for this instance of nsHttpChannelnsresultnsHttpChannel::GenerateCacheKey(uint32_tpostID,nsACString&cacheKey){AssembleCacheKey(mFallbackChannel?mFallbackKey.get():mSpec.get(),postID,cacheKey);returnNS_OK;}// Assembles a cache-key from the given pieces of information and |mLoadFlags|voidnsHttpChannel::AssembleCacheKey(constchar*spec,uint32_tpostID,nsACString&cacheKey){cacheKey.Truncate();if(mLoadFlags&LOAD_ANONYMOUS){cacheKey.AssignLiteral("anon&");}if(postID){charbuf[32];SprintfLiteral(buf,"id=%x&",postID);cacheKey.Append(buf);}if(!cacheKey.IsEmpty()){cacheKey.AppendLiteral("uri=");}// Strip any trailing #ref from the URL before using it as the keyconstchar*p=strchr(spec,'#');if(p)cacheKey.Append(spec,p-spec);elsecacheKey.Append(spec);}nsresultDoUpdateExpirationTime(nsHttpChannel*aSelf,nsICacheEntry*aCacheEntry,nsHttpResponseHead*aResponseHead,uint32_t&aExpirationTime){MOZ_ASSERT(aExpirationTime==0);NS_ENSURE_TRUE(aResponseHead,NS_ERROR_FAILURE);nsresultrv;if(!aResponseHead->MustValidate()){uint32_tfreshnessLifetime=0;rv=aResponseHead->ComputeFreshnessLifetime(&freshnessLifetime);if(NS_FAILED(rv))returnrv;if(freshnessLifetime>0){uint32_tnow=NowInSeconds(),currentAge=0;rv=aResponseHead->ComputeCurrentAge(now,aSelf->GetRequestTime(),¤tAge);if(NS_FAILED(rv))returnrv;LOG(("freshnessLifetime = %u, currentAge = %u\n",freshnessLifetime,currentAge));if(freshnessLifetime>currentAge){uint32_ttimeRemaining=freshnessLifetime-currentAge;// be careful... now + timeRemaining may overflowif(now+timeRemaining<now)aExpirationTime=uint32_t(-1);elseaExpirationTime=now+timeRemaining;}elseaExpirationTime=0;}}rv=aCacheEntry->SetExpirationTime(aExpirationTime);NS_ENSURE_SUCCESS(rv,rv);returnrv;}// UpdateExpirationTime is called when a new response comes in from the server.// It updates the stored response-time and sets the expiration time on the// cache entry.//// From section 13.2.4 of RFC2616, we compute expiration time as follows://// timeRemaining = freshnessLifetime - currentAge// expirationTime = now + timeRemaining//nsresultnsHttpChannel::UpdateExpirationTime(){uint32_texpirationTime=0;nsresultrv=DoUpdateExpirationTime(this,mCacheEntry,mResponseHead,expirationTime);NS_ENSURE_SUCCESS(rv,rv);if(mOfflineCacheEntry){rv=mOfflineCacheEntry->SetExpirationTime(expirationTime);NS_ENSURE_SUCCESS(rv,rv);}returnNS_OK;}/*static*/inlineboolnsHttpChannel::HasQueryString(nsHttpRequestHead::ParsedMethodTypemethod,nsIURI*uri){// Must be called on the main thread because nsIURI does not implement// thread-safe QueryInterface.MOZ_ASSERT(NS_IsMainThread());if(method!=nsHttpRequestHead::kMethod_Get&&method!=nsHttpRequestHead::kMethod_Head)returnfalse;nsAutoCStringquery;nsCOMPtr<nsIURL>url=do_QueryInterface(uri);nsresultrv=url->GetQuery(query);returnNS_SUCCEEDED(rv)&&!query.IsEmpty();}boolnsHttpChannel::ShouldUpdateOfflineCacheEntry(){if(!mApplicationCacheForWrite||!mOfflineCacheEntry){returnfalse;}// if we're updating the cache entry, update the offline cache entry tooif(mCacheEntry&&mCacheEntryIsWriteOnly){returntrue;}// if there's nothing in the offline cache, add itif(mOfflineCacheEntry){returntrue;}// if the document is newer than the offline entry, update ituint32_tdocLastModifiedTime;nsresultrv=mResponseHead->GetLastModifiedValue(&docLastModifiedTime);if(NS_FAILED(rv)){returntrue;}if(mOfflineCacheLastModifiedTime==0){returnfalse;}if(docLastModifiedTime>mOfflineCacheLastModifiedTime){returntrue;}returnfalse;}nsresultnsHttpChannel::OpenCacheInputStream(nsICacheEntry*cacheEntry,boolstartBuffering,boolcheckingAppCacheEntry){nsresultrv;boolisHttps=false;rv=mURI->SchemeIs("https",&isHttps);NS_ENSURE_SUCCESS(rv,rv);if(isHttps){rv=cacheEntry->GetSecurityInfo(getter_AddRefs(mCachedSecurityInfo));if(NS_FAILED(rv)){LOG(("failed to parse security-info [channel=%p, entry=%p]",this,cacheEntry));NS_WARNING("failed to parse security-info");cacheEntry->AsyncDoom(nullptr);returnrv;}// XXX: We should not be skilling this check in the offline cache// case, but we have to do so now to work around bug 794507.boolmustHaveSecurityInfo=!mLoadedFromApplicationCache&&!checkingAppCacheEntry;MOZ_ASSERT(mCachedSecurityInfo||!mustHaveSecurityInfo);if(!mCachedSecurityInfo&&mustHaveSecurityInfo){LOG(("mCacheEntry->GetSecurityInfo returned success but did not ""return the security info [channel=%p, entry=%p]",this,cacheEntry));cacheEntry->AsyncDoom(nullptr);returnNS_ERROR_UNEXPECTED;// XXX error code}}// Keep the conditions below in sync with the conditions in ReadFromCache.rv=NS_OK;if(WillRedirect(mCachedResponseHead)){// Do not even try to read the entity for a redirect because we do not// return an entity to the application when we process redirects.LOG(("Will skip read of cached redirect entity\n"));returnNS_OK;}if((mLoadFlags&nsICachingChannel::LOAD_ONLY_IF_MODIFIED)&&!mCachedContentIsPartial){// For LOAD_ONLY_IF_MODIFIED, we usually don't have to deal with the// cached entity.if(!mApplicationCacheForWrite){LOG(("Will skip read from cache based on LOAD_ONLY_IF_MODIFIED ""load flag\n"));returnNS_OK;}// If offline caching has been requested and the offline cache needs// updating, we must complete the call even if the main cache entry// is up to date. We don't know yet for sure whether the offline// cache needs updating because at this point we haven't opened it// for writing yet, so we have to start reading the cached entity now// just in case.LOG(("May skip read from cache based on LOAD_ONLY_IF_MODIFIED ""load flag\n"));}// Open an input stream for the entity, so that the call to OpenInputStream// happens off the main thread.nsCOMPtr<nsIInputStream>stream;// If an alternate representation was requested, try to open the alt// input stream.if(!mPreferredCachedAltDataType.IsEmpty()){rv=cacheEntry->OpenAlternativeInputStream(mPreferredCachedAltDataType,getter_AddRefs(stream));if(NS_SUCCEEDED(rv)){// We have succeeded.mAvailableCachedAltDataType=mPreferredCachedAltDataType;// Set the correct data size on the channel.int64_taltDataSize;if(NS_SUCCEEDED(cacheEntry->GetAltDataSize(&altDataSize))){mAltDataLength=altDataSize;}}}if(!stream){rv=cacheEntry->OpenInputStream(0,getter_AddRefs(stream));}if(NS_FAILED(rv)){LOG(("Failed to open cache input stream [channel=%p, ""mCacheEntry=%p]",this,cacheEntry));returnrv;}if(startBuffering){boolnonBlocking;rv=stream->IsNonBlocking(&nonBlocking);if(NS_SUCCEEDED(rv)&&nonBlocking)startBuffering=false;}if(!startBuffering){// Bypass wrapping the input stream for the new cache back-end since// nsIStreamTransportService expects a blocking stream. Preloading of// the data must be done on the level of the cache backend, internally.//// We do not connect the stream to the stream transport service if we// have to validate the entry with the server. If we did, we would get// into a race condition between the stream transport service reading// the existing contents and the opening of the cache entry's output// stream to write the new contents in the case where we get a non-304// response.LOG(("Opened cache input stream without buffering [channel=%p, ""mCacheEntry=%p, stream=%p]",this,cacheEntry,stream.get()));mCacheInputStream.takeOver(stream);returnrv;}// Have the stream transport service start reading the entity on one of its// background threads.nsCOMPtr<nsITransport>transport;nsCOMPtr<nsIInputStream>wrapper;nsCOMPtr<nsIStreamTransportService>sts(services::GetStreamTransportService());rv=sts?NS_OK:NS_ERROR_NOT_AVAILABLE;if(NS_SUCCEEDED(rv)){rv=sts->CreateInputTransport(stream,int64_t(-1),int64_t(-1),true,getter_AddRefs(transport));}if(NS_SUCCEEDED(rv)){rv=transport->OpenInputStream(0,0,0,getter_AddRefs(wrapper));}if(NS_SUCCEEDED(rv)){LOG(("Opened cache input stream [channel=%p, wrapper=%p, ""transport=%p, stream=%p]",this,wrapper.get(),transport.get(),stream.get()));}else{LOG(("Failed to open cache input stream [channel=%p, ""wrapper=%p, transport=%p, stream=%p]",this,wrapper.get(),transport.get(),stream.get()));stream->Close();returnrv;}mCacheInputStream.takeOver(wrapper);returnNS_OK;}// Actually process the cached response that we started to handle in CheckCache// and/or StartBufferingCachedEntity.nsresultnsHttpChannel::ReadFromCache(boolalreadyMarkedValid){NS_ENSURE_TRUE(mCacheEntry,NS_ERROR_FAILURE);NS_ENSURE_TRUE(mCachedContentIsValid,NS_ERROR_FAILURE);NS_ENSURE_TRUE(!mCachePump,NS_OK);// already openedLOG(("nsHttpChannel::ReadFromCache [this=%p] ""Using cached copy of: %s\n",this,mSpec.get()));if(mRaceCacheWithNetwork){MOZ_ASSERT(mFirstResponseSource!=RESPONSE_FROM_CACHE);if(mFirstResponseSource==RESPONSE_PENDING){LOG(("First response from cache\n"));mFirstResponseSource=RESPONSE_FROM_CACHE;// Cancel the transaction because we will serve the request from the cacheCancelNetworkRequest(NS_BINDING_ABORTED);if(mTransactionPump&&mSuspendCount){uint32_tsuspendCount=mSuspendCount;while(suspendCount--){mTransactionPump->Resume();}}mTransaction=nullptr;mTransactionPump=nullptr;}else{MOZ_ASSERT(mFirstResponseSource==RESPONSE_FROM_NETWORK);LOG(("Skipping read from cache because first response was from network\n"));if(!mOnCacheEntryCheckTimestamp.IsNull()){TimeStampcurrentTime=TimeStamp::Now();int64_tsavedTime=(currentTime-mOnStartRequestTimestamp).ToMilliseconds();Telemetry::Accumulate(Telemetry::NETWORK_RACE_CACHE_WITH_NETWORK_SAVED_TIME,savedTime);int64_tdiffTime=(currentTime-mOnCacheEntryCheckTimestamp).ToMilliseconds();Telemetry::Accumulate(Telemetry::NETWORK_RACE_CACHE_WITH_NETWORK_OCEC_ON_START_DIFF,diffTime);}returnNS_OK;}}if(mCachedResponseHead)mResponseHead=Move(mCachedResponseHead);UpdateInhibitPersistentCachingFlag();// if we don't already have security info, try to get it from the cache// entry. there are two cases to consider here: 1) we are just reading// from the cache, or 2) this may be due to a 304 not modified response,// in which case we could have security info from a socket transport.if(!mSecurityInfo)mSecurityInfo=mCachedSecurityInfo;if(!alreadyMarkedValid&&!mCachedContentIsPartial){// We validated the entry, and we have write access to the cache, so// mark the cache entry as valid in order to allow others access to// this cache entry.//// TODO: This should be done asynchronously so we don't take the cache// service lock on the main thread.mCacheEntry->MaybeMarkValid();}nsresultrv;// Keep the conditions below in sync with the conditions in// StartBufferingCachedEntity.if(WillRedirect(mResponseHead)){// TODO: Bug 759040 - We should call HandleAsyncRedirect directly here,// to avoid event dispatching latency.MOZ_ASSERT(!mCacheInputStream);LOG(("Skipping skip read of cached redirect entity\n"));returnAsyncCall(&nsHttpChannel::HandleAsyncRedirect);}if((mLoadFlags&LOAD_ONLY_IF_MODIFIED)&&!mCachedContentIsPartial){if(!mApplicationCacheForWrite){LOG(("Skipping read from cache based on LOAD_ONLY_IF_MODIFIED ""load flag\n"));MOZ_ASSERT(!mCacheInputStream);// TODO: Bug 759040 - We should call HandleAsyncNotModified directly// here, to avoid event dispatching latency.returnAsyncCall(&nsHttpChannel::HandleAsyncNotModified);}if(!ShouldUpdateOfflineCacheEntry()){LOG(("Skipping read from cache based on LOAD_ONLY_IF_MODIFIED ""load flag (mApplicationCacheForWrite not null case)\n"));mCacheInputStream.CloseAndRelease();// TODO: Bug 759040 - We should call HandleAsyncNotModified directly// here, to avoid event dispatching latency.returnAsyncCall(&nsHttpChannel::HandleAsyncNotModified);}}MOZ_ASSERT(mCacheInputStream);if(!mCacheInputStream){NS_ERROR("mCacheInputStream is null but we're expecting to ""be able to read from it.");returnNS_ERROR_UNEXPECTED;}nsCOMPtr<nsIInputStream>inputStream=mCacheInputStream.forget();rv=nsInputStreamPump::Create(getter_AddRefs(mCachePump),inputStream,int64_t(-1),int64_t(-1),0,0,true);if(NS_FAILED(rv)){inputStream->Close();returnrv;}rv=mCachePump->AsyncRead(this,mListenerContext);if(NS_FAILED(rv))returnrv;if(mTimingEnabled)mCacheReadStart=TimeStamp::Now();uint32_tsuspendCount=mSuspendCount;while(suspendCount--)mCachePump->Suspend();returnNS_OK;}voidnsHttpChannel::CloseCacheEntry(booldoomOnFailure){mCacheInputStream.CloseAndRelease();if(!mCacheEntry)return;LOG(("nsHttpChannel::CloseCacheEntry [this=%p] mStatus=%"PRIx32" mCacheEntryIsWriteOnly=%x",this,static_cast<uint32_t>(static_cast<nsresult>(mStatus)),mCacheEntryIsWriteOnly));// If we have begun to create or replace a cache entry, and that cache// entry is not complete and not resumable, then it needs to be doomed.// Otherwise, CheckCache will make the mistake of thinking that the// partial cache entry is complete.booldoom=false;if(mInitedCacheEntry){MOZ_ASSERT(mResponseHead,"oops");if(NS_FAILED(mStatus)&&doomOnFailure&&mCacheEntryIsWriteOnly&&!mResponseHead->IsResumable())doom=true;}elseif(mCacheEntryIsWriteOnly)doom=true;if(doom){LOG((" dooming cache entry!!"));mCacheEntry->AsyncDoom(nullptr);}else{// Store updated security info, makes cached EV status race less likely// (see bug 1040086)if(mSecurityInfo)mCacheEntry->SetSecurityInfo(mSecurityInfo);}mCachedResponseHead=nullptr;mCachePump=nullptr;mCacheEntry=nullptr;mCacheEntryIsWriteOnly=false;mInitedCacheEntry=false;}voidnsHttpChannel::CloseOfflineCacheEntry(){if(!mOfflineCacheEntry)return;LOG(("nsHttpChannel::CloseOfflineCacheEntry [this=%p]",this));if(NS_FAILED(mStatus)){mOfflineCacheEntry->AsyncDoom(nullptr);}else{boolsucceeded;if(NS_SUCCEEDED(GetRequestSucceeded(&succeeded))&&!succeeded)mOfflineCacheEntry->AsyncDoom(nullptr);}mOfflineCacheEntry=nullptr;}// Initialize the cache entry for writing.// - finalize storage policy// - store security info// - update expiration time// - store headers and other meta datansresultnsHttpChannel::InitCacheEntry(){nsresultrv;NS_ENSURE_TRUE(mCacheEntry,NS_ERROR_UNEXPECTED);// if only reading, nothing to be done here.if(mCacheEntryIsReadOnly)returnNS_OK;// Don't cache the response again if already cached...if(mCachedContentIsValid)returnNS_OK;LOG(("nsHttpChannel::InitCacheEntry [this=%p entry=%p]\n",this,mCacheEntry.get()));boolrecreate=!mCacheEntryIsWriteOnly;booldontPersist=mLoadFlags&INHIBIT_PERSISTENT_CACHING;if(!recreate&&dontPersist){// If the current entry is persistent but we inhibit peristence// then force recreation of the entry as memory/only.rv=mCacheEntry->GetPersistent(&recreate);if(NS_FAILED(rv))returnrv;}if(recreate){LOG((" we have a ready entry, but reading it again from the server -> recreating cache entry\n"));// clean the altData cache and reset this to avoid wrong content lengthmAvailableCachedAltDataType.Truncate();nsCOMPtr<nsICacheEntry>currentEntry;currentEntry.swap(mCacheEntry);rv=currentEntry->Recreate(dontPersist,getter_AddRefs(mCacheEntry));if(NS_FAILED(rv)){LOG((" recreation failed, the response will not be cached"));returnNS_OK;}mCacheEntryIsWriteOnly=true;}// Set the expiration time for this cache entryrv=UpdateExpirationTime();if(NS_FAILED(rv))returnrv;// mark this weakly framed until a response body is seenmCacheEntry->SetMetaDataElement("strongly-framed","0");rv=AddCacheEntryHeaders(mCacheEntry);if(NS_FAILED(rv))returnrv;mInitedCacheEntry=true;// Don't perform the check when writing (doesn't make sense)mConcurrentCacheAccess=0;returnNS_OK;}voidnsHttpChannel::UpdateInhibitPersistentCachingFlag(){// The no-store directive within the 'Cache-Control:' header indicates// that we must not store the response in a persistent cache.if(mResponseHead->NoStore())mLoadFlags|=INHIBIT_PERSISTENT_CACHING;// Only cache SSL content on disk if the pref is setboolisHttps;if(!gHttpHandler->IsPersistentHttpsCachingEnabled()&&NS_SUCCEEDED(mURI->SchemeIs("https",&isHttps))&&isHttps){mLoadFlags|=INHIBIT_PERSISTENT_CACHING;}}nsresultnsHttpChannel::InitOfflineCacheEntry(){// This function can be called even when we fail to connect (bug 551990)if(!mOfflineCacheEntry){returnNS_OK;}if(!mResponseHead||mResponseHead->NoStore()){if(mResponseHead&&mResponseHead->NoStore()){mOfflineCacheEntry->AsyncDoom(nullptr);}CloseOfflineCacheEntry();if(mResponseHead&&mResponseHead->NoStore()){returnNS_ERROR_NOT_AVAILABLE;}returnNS_OK;}// This entry's expiration time should match the main entry's expiration// time. UpdateExpirationTime() will keep it in sync once the offline// cache entry has been created.if(mCacheEntry){uint32_texpirationTime;nsresultrv=mCacheEntry->GetExpirationTime(&expirationTime);NS_ENSURE_SUCCESS(rv,rv);mOfflineCacheEntry->SetExpirationTime(expirationTime);}returnAddCacheEntryHeaders(mOfflineCacheEntry);}nsresultDoAddCacheEntryHeaders(nsHttpChannel*self,nsICacheEntry*entry,nsHttpRequestHead*requestHead,nsHttpResponseHead*responseHead,nsISupports*securityInfo){nsresultrv;LOG(("nsHttpChannel::AddCacheEntryHeaders [this=%p] begin",self));// Store secure data in memory onlyif(securityInfo)entry->SetSecurityInfo(securityInfo);// Store the HTTP request method with the cache entry so we can distinguish// for example GET and HEAD responses.nsAutoCStringmethod;requestHead->Method(method);rv=entry->SetMetaDataElement("request-method",method.get());if(NS_FAILED(rv))returnrv;// Store the HTTP authorization scheme used if any...rv=StoreAuthorizationMetaData(entry,requestHead);if(NS_FAILED(rv))returnrv;// Iterate over the headers listed in the Vary response header, and// store the value of the corresponding request header so we can verify// that it has not varied when we try to re-use the cached response at// a later time. Take care to store "Cookie" headers only as hashes// due to security considerations and the fact that they can be pretty// large (bug 468426). We take care of "Vary: cookie" in ResponseWouldVary.//// NOTE: if "Vary: accept, cookie", then we will store the "accept" header// in the cache. we could try to avoid needlessly storing the "accept"// header in this case, but it doesn't seem worth the extra code to perform// the check.{nsAutoCStringbuf,metaKey;Unused<<responseHead->GetHeader(nsHttp::Vary,buf);if(!buf.IsEmpty()){NS_NAMED_LITERAL_CSTRING(prefix,"request-");char*bufData=buf.BeginWriting();// going to munge bufchar*token=nsCRT::strtok(bufData,NS_HTTP_HEADER_SEPS,&bufData);while(token){LOG(("nsHttpChannel::AddCacheEntryHeaders [this=%p] " \"processing %s",self,token));if(*token!='*'){nsHttpAtomatom=nsHttp::ResolveAtom(token);nsAutoCStringval;nsAutoCStringhash;if(NS_SUCCEEDED(requestHead->GetHeader(atom,val))){// If cookie-header, store a hash of the valueif(atom==nsHttp::Cookie){LOG(("nsHttpChannel::AddCacheEntryHeaders [this=%p] " \"cookie-value %s",self,val.get()));rv=Hash(val.get(),hash);// If hash failed, store a string not very likely// to be the result of subsequent hashesif(NS_FAILED(rv)){val=NS_LITERAL_CSTRING("<hash failed>");}else{val=hash;}LOG((" hashed to %s\n",val.get()));}// build cache meta data key and set meta data element...metaKey=prefix+nsDependentCString(token);entry->SetMetaDataElement(metaKey.get(),val.get());}else{LOG(("nsHttpChannel::AddCacheEntryHeaders [this=%p] " \"clearing metadata for %s",self,token));metaKey=prefix+nsDependentCString(token);entry->SetMetaDataElement(metaKey.get(),nullptr);}}token=nsCRT::strtok(bufData,NS_HTTP_HEADER_SEPS,&bufData);}}}// Store the received HTTP head with the cache entry as an element of// the meta data.nsAutoCStringhead;responseHead->Flatten(head,true);rv=entry->SetMetaDataElement("response-head",head.get());if(NS_FAILED(rv))returnrv;head.Truncate();responseHead->FlattenNetworkOriginalHeaders(head);rv=entry->SetMetaDataElement("original-response-headers",head.get());if(NS_FAILED(rv))returnrv;// Indicate we have successfully finished setting metadata on the cache entry.rv=entry->MetaDataReady();returnrv;}nsresultnsHttpChannel::AddCacheEntryHeaders(nsICacheEntry*entry){returnDoAddCacheEntryHeaders(this,entry,&mRequestHead,mResponseHead,mSecurityInfo);}inlinevoidGetAuthType(constchar*challenge,nsCString&authType){constchar*p;// get the challenge typeif((p=strchr(challenge,' '))!=nullptr)authType.Assign(challenge,p-challenge);elseauthType.Assign(challenge);}nsresultStoreAuthorizationMetaData(nsICacheEntry*entry,nsHttpRequestHead*requestHead){// Not applicable to proxy authorization...nsAutoCStringval;if(NS_FAILED(requestHead->GetHeader(nsHttp::Authorization,val))){returnNS_OK;}// eg. [Basic realm="wally world"]nsAutoCStringbuf;GetAuthType(val.get(),buf);returnentry->SetMetaDataElement("auth",buf.get());}// Finalize the cache entry// - may need to rewrite response headers if any headers changed// - may need to recalculate the expiration time if any headers changed// - called only for freshly written cache entriesnsresultnsHttpChannel::FinalizeCacheEntry(){LOG(("nsHttpChannel::FinalizeCacheEntry [this=%p]\n",this));// Don't update this meta-data on 304if(mStronglyFramed&&!mCachedContentIsValid&&mCacheEntry){LOG(("nsHttpChannel::FinalizeCacheEntry [this=%p] Is Strongly Framed\n",this));mCacheEntry->SetMetaDataElement("strongly-framed","1");}if(mResponseHead&&mResponseHeadersModified){// Set the expiration time for this cache entrynsresultrv=UpdateExpirationTime();if(NS_FAILED(rv))returnrv;}returnNS_OK;}// Open an output stream to the cache entry and insert a listener tee into// the chain of response listeners.nsresultnsHttpChannel::InstallCacheListener(int64_toffset){nsresultrv;LOG(("Preparing to write data into the cache [uri=%s]\n",mSpec.get()));MOZ_ASSERT(mCacheEntry);MOZ_ASSERT(mCacheEntryIsWriteOnly||mCachedContentIsPartial||mRaceCacheWithNetwork);MOZ_ASSERT(mListener);nsAutoCStringcontentEncoding,contentType;Unused<<mResponseHead->GetHeader(nsHttp::Content_Encoding,contentEncoding);mResponseHead->ContentType(contentType);// If the content is compressible and the server has not compressed it,// mark the cache entry for compression.if(contentEncoding.IsEmpty()&&(contentType.EqualsLiteral(TEXT_HTML)||contentType.EqualsLiteral(TEXT_PLAIN)||contentType.EqualsLiteral(TEXT_CSS)||contentType.EqualsLiteral(TEXT_JAVASCRIPT)||contentType.EqualsLiteral(TEXT_ECMASCRIPT)||contentType.EqualsLiteral(TEXT_XML)||contentType.EqualsLiteral(APPLICATION_JAVASCRIPT)||contentType.EqualsLiteral(APPLICATION_ECMASCRIPT)||contentType.EqualsLiteral(APPLICATION_XJAVASCRIPT)||contentType.EqualsLiteral(APPLICATION_XHTML_XML))){rv=mCacheEntry->SetMetaDataElement("uncompressed-len","0");if(NS_FAILED(rv)){LOG(("unable to mark cache entry for compression"));}}LOG(("Trading cache input stream for output stream [channel=%p]",this));// We must close the input stream first because cache entries do not// correctly handle having an output stream and input streams open at// the same time.mCacheInputStream.CloseAndRelease();nsCOMPtr<nsIOutputStream>out;rv=mCacheEntry->OpenOutputStream(offset,getter_AddRefs(out));if(rv==NS_ERROR_NOT_AVAILABLE){LOG((" entry doomed, not writing it [channel=%p]",this));// Entry is already doomed.// This may happen when expiration time is set to past and the entry// has been removed by the background eviction logic.returnNS_OK;}if(NS_FAILED(rv))returnrv;if(mCacheOnlyMetadata){LOG(("Not storing content, cacheOnlyMetadata set"));// We must open and then close the output stream of the cache entry.// This way we indicate the content has been written (despite with zero// length) and the entry is now in the ready state with "having data".out->Close();returnNS_OK;}// XXX disk cache does not support overlapped i/o yet#if 0 // Mark entry valid inorder to allow simultaneous reading... rv = mCacheEntry->MarkValid(); if (NS_FAILED(rv)) return rv;#endifnsCOMPtr<nsIStreamListenerTee>tee=do_CreateInstance(kStreamListenerTeeCID,&rv);if(NS_FAILED(rv))returnrv;nsCOMPtr<nsIEventTarget>cacheIOTarget;if(!CacheObserver::UseNewCache()){nsCOMPtr<nsICacheStorageService>serv(services::GetCacheStorageService());if(!serv){returnNS_ERROR_NOT_AVAILABLE;}serv->GetIoTarget(getter_AddRefs(cacheIOTarget));}if(!cacheIOTarget){LOG(("nsHttpChannel::InstallCacheListener sync tee %p rv=%"PRIx32" cacheIOTarget=%p",tee.get(),static_cast<uint32_t>(rv),cacheIOTarget.get()));rv=tee->Init(mListener,out,nullptr);}else{LOG(("nsHttpChannel::InstallCacheListener async tee %p",tee.get()));rv=tee->InitAsync(mListener,cacheIOTarget,out,nullptr);}if(NS_FAILED(rv))returnrv;mListener=tee;returnNS_OK;}nsresultnsHttpChannel::InstallOfflineCacheListener(int64_toffset){nsresultrv;LOG(("Preparing to write data into the offline cache [uri=%s]\n",mSpec.get()));MOZ_ASSERT(mOfflineCacheEntry);MOZ_ASSERT(mListener);nsCOMPtr<nsIOutputStream>out;rv=mOfflineCacheEntry->OpenOutputStream(offset,getter_AddRefs(out));if(NS_FAILED(rv))returnrv;nsCOMPtr<nsIStreamListenerTee>tee=do_CreateInstance(kStreamListenerTeeCID,&rv);if(NS_FAILED(rv))returnrv;rv=tee->Init(mListener,out,nullptr);if(NS_FAILED(rv))returnrv;mListener=tee;returnNS_OK;}voidnsHttpChannel::ClearBogusContentEncodingIfNeeded(){// For .gz files, apache sends both a Content-Type: application/x-gzip// as well as Content-Encoding: gzip, which is completely wrong. In// this case, we choose to ignore the rogue Content-Encoding header. We// must do this early on so as to prevent it from being seen up stream.// The same problem exists for Content-Encoding: compress in default// Apache installs.nsAutoCStringcontentType;mResponseHead->ContentType(contentType);if(mResponseHead->HasHeaderValue(nsHttp::Content_Encoding,"gzip")&&(contentType.EqualsLiteral(APPLICATION_GZIP)||contentType.EqualsLiteral(APPLICATION_GZIP2)||contentType.EqualsLiteral(APPLICATION_GZIP3))){// clear the Content-Encoding headermResponseHead->ClearHeader(nsHttp::Content_Encoding);}elseif(mResponseHead->HasHeaderValue(nsHttp::Content_Encoding,"compress")&&(contentType.EqualsLiteral(APPLICATION_COMPRESS)||contentType.EqualsLiteral(APPLICATION_COMPRESS2))){// clear the Content-Encoding headermResponseHead->ClearHeader(nsHttp::Content_Encoding);}}//-----------------------------------------------------------------------------// nsHttpChannel <redirect>//-----------------------------------------------------------------------------nsresultnsHttpChannel::SetupReplacementChannel(nsIURI*newURI,nsIChannel*newChannel,boolpreserveMethod,uint32_tredirectFlags){LOG(("nsHttpChannel::SetupReplacementChannel ""[this=%p newChannel=%p preserveMethod=%d]",this,newChannel,preserveMethod));nsresultrv=HttpBaseChannel::SetupReplacementChannel(newURI,newChannel,preserveMethod,redirectFlags);if(NS_FAILED(rv))returnrv;nsCOMPtr<nsIHttpChannel>httpChannel=do_QueryInterface(newChannel);if(!httpChannel)returnNS_OK;// no other options to set// convey the mApplyConversion flag (bug 91862)nsCOMPtr<nsIEncodedChannel>encodedChannel=do_QueryInterface(httpChannel);if(encodedChannel)encodedChannel->SetApplyConversion(mApplyConversion);// transfer the resume informationif(mResuming){nsCOMPtr<nsIResumableChannel>resumableChannel(do_QueryInterface(newChannel));if(!resumableChannel){NS_WARNING("Got asked to resume, but redirected to non-resumable channel!");returnNS_ERROR_NOT_RESUMABLE;}resumableChannel->ResumeAt(mStartPos,mEntityID);}if(!(redirectFlags&nsIChannelEventSink::REDIRECT_STS_UPGRADE)&&mInterceptCache!=INTERCEPTED&&mRedirectMode!=nsIHttpChannelInternal::REDIRECT_MODE_MANUAL){nsLoadFlagsloadFlags=nsIRequest::LOAD_NORMAL;rv=newChannel->GetLoadFlags(&loadFlags);NS_ENSURE_SUCCESS(rv,rv);loadFlags|=nsIChannel::LOAD_BYPASS_SERVICE_WORKER;rv=newChannel->SetLoadFlags(loadFlags);NS_ENSURE_SUCCESS(rv,rv);}if(redirectFlags&nsIChannelEventSink::REDIRECT_INTERNAL){nsCOMPtr<nsITimedChannel>timedChannel=do_QueryInterface(newChannel);if(timedChannel){timedChannel->SetLaunchServiceWorkerStart(mLaunchServiceWorkerStart);timedChannel->SetLaunchServiceWorkerEnd(mLaunchServiceWorkerEnd);timedChannel->SetDispatchFetchEventStart(mDispatchFetchEventStart);timedChannel->SetDispatchFetchEventEnd(mDispatchFetchEventEnd);timedChannel->SetHandleFetchEventStart(mHandleFetchEventStart);timedChannel->SetHandleFetchEventEnd(mHandleFetchEventEnd);}}returnNS_OK;}nsresultnsHttpChannel::AsyncProcessRedirection(uint32_tredirectType){LOG(("nsHttpChannel::AsyncProcessRedirection [this=%p type=%u]\n",this,redirectType));nsAutoCStringlocation;// if a location header was not given, then we can't perform the redirect,// so just carry on as though this were a normal response.if(NS_FAILED(mResponseHead->GetHeader(nsHttp::Location,location)))returnNS_ERROR_FAILURE;// make sure non-ASCII characters in the location header are escaped.nsAutoCStringlocationBuf;if(NS_EscapeURL(location.get(),-1,esc_OnlyNonASCII,locationBuf))location=locationBuf;if(mRedirectionLimit==0){LOG(("redirection limit reached!\n"));returnNS_ERROR_REDIRECT_LOOP;}mRedirectType=redirectType;LOG(("redirecting to: %s [redirection-limit=%u]\n",location.get(),uint32_t(mRedirectionLimit)));nsresultrv=CreateNewURI(location.get(),getter_AddRefs(mRedirectURI));if(NS_FAILED(rv)){LOG(("Invalid URI for redirect: Location: %s\n",location.get()));returnNS_ERROR_CORRUPTED_CONTENT;}if(mApplicationCache){// if we are redirected to a different origin check if there is a fallback// cache entry to fall back to. we don't care about file strict// checking, at least mURI is not a file URI.if(!NS_SecurityCompareURIs(mURI,mRedirectURI,false)){PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirectionAfterFallback);boolwaitingForRedirectCallback;Unused<<ProcessFallback(&waitingForRedirectCallback);if(waitingForRedirectCallback)returnNS_OK;PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirectionAfterFallback);}}returnContinueProcessRedirectionAfterFallback(NS_OK);}nsresultnsHttpChannel::ContinueProcessRedirectionAfterFallback(nsresultrv){if(NS_SUCCEEDED(rv)&&mFallingBack){// do not continue with redirect processing, fallback is in// progress now.returnNS_OK;}// Kill the current cache entry if we are redirecting// back to ourself.boolredirectingBackToSameURI=false;if(mCacheEntry&&mCacheEntryIsWriteOnly&&NS_SUCCEEDED(mURI->Equals(mRedirectURI,&redirectingBackToSameURI))&&redirectingBackToSameURI)mCacheEntry->AsyncDoom(nullptr);boolhasRef=false;rv=mRedirectURI->GetHasRef(&hasRef);// move the reference of the old location to the new one if the new// one has none.if(NS_SUCCEEDED(rv)&&!hasRef){nsAutoCStringref;mURI->GetRef(ref);if(!ref.IsEmpty()){// NOTE: SetRef will fail if mRedirectURI is immutable// (e.g. an about: URI)... Oh well.mRedirectURI->SetRef(ref);}}boolrewriteToGET=ShouldRewriteRedirectToGET(mRedirectType,mRequestHead.ParsedMethod());// prompt if the method is not safe (such as POST, PUT, DELETE, ...)if(!rewriteToGET&&!mRequestHead.IsSafeMethod()){rv=PromptTempRedirect();if(NS_FAILED(rv))returnrv;}nsCOMPtr<nsIIOService>ioService;rv=gHttpHandler->GetIOService(getter_AddRefs(ioService));if(NS_FAILED(rv))returnrv;uint32_tredirectFlags;if(nsHttp::IsPermanentRedirect(mRedirectType))redirectFlags=nsIChannelEventSink::REDIRECT_PERMANENT;elseredirectFlags=nsIChannelEventSink::REDIRECT_TEMPORARY;nsCOMPtr<nsIChannel>newChannel;nsCOMPtr<nsILoadInfo>redirectLoadInfo=CloneLoadInfoForRedirect(mRedirectURI,redirectFlags);rv=NS_NewChannelInternal(getter_AddRefs(newChannel),mRedirectURI,redirectLoadInfo,nullptr,// aLoadGroupnullptr,// aCallbacksnsIRequest::LOAD_NORMAL,ioService);NS_ENSURE_SUCCESS(rv,rv);rv=SetupReplacementChannel(mRedirectURI,newChannel,!rewriteToGET,redirectFlags);if(NS_FAILED(rv))returnrv;// verify that this is a legal redirectmRedirectChannel=newChannel;PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirection);rv=gHttpHandler->AsyncOnChannelRedirect(this,newChannel,redirectFlags);if(NS_SUCCEEDED(rv))rv=WaitForRedirectCallback();if(NS_FAILED(rv)){AutoRedirectVetoNotifiernotifier(this);PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirection);}returnrv;}nsresultnsHttpChannel::ContinueProcessRedirection(nsresultrv){AutoRedirectVetoNotifiernotifier(this);LOG(("nsHttpChannel::ContinueProcessRedirection [rv=%"PRIx32",this=%p]\n",static_cast<uint32_t>(rv),this));if(NS_FAILED(rv))returnrv;NS_PRECONDITION(mRedirectChannel,"No redirect channel?");// Make sure to do this after we received redirect veto answer,// i.e. after all sinks had been notifiedmRedirectChannel->SetOriginalURI(mOriginalURI);// And now, the deprecated waynsCOMPtr<nsIHttpEventSink>httpEventSink;GetCallback(httpEventSink);if(httpEventSink){// NOTE: nsIHttpEventSink is only used for compatibility with pre-1.8// versions.rv=httpEventSink->OnRedirect(this,mRedirectChannel);if(NS_FAILED(rv))returnrv;}// XXX we used to talk directly with the script security manager, but that// should really be handled by the event sink implementation.// begin loading the new channelif(mLoadInfo&&mLoadInfo->GetEnforceSecurity()){MOZ_ASSERT(!mListenerContext,"mListenerContext should be null!");rv=mRedirectChannel->AsyncOpen2(mListener);}else{rv=mRedirectChannel->AsyncOpen(mListener,mListenerContext);}NS_ENSURE_SUCCESS(rv,rv);// close down this channelCancel(NS_BINDING_REDIRECTED);notifier.RedirectSucceeded();ReleaseListeners();returnNS_OK;}//-----------------------------------------------------------------------------// nsHttpChannel <auth>//-----------------------------------------------------------------------------NS_IMETHODIMPnsHttpChannel::OnAuthAvailable(){LOG(("nsHttpChannel::OnAuthAvailable [this=%p]",this));// setting mAuthRetryPending flag and resuming the transaction// triggers process of throwing away the unauthenticated data already// coming from the networkmAuthRetryPending=true;mProxyAuthPending=false;LOG(("Resuming the transaction, we got credentials from user"));mTransactionPump->Resume();returnNS_OK;}NS_IMETHODIMPnsHttpChannel::OnAuthCancelled(booluserCancel){LOG(("nsHttpChannel::OnAuthCancelled [this=%p]",this));if(mTransactionPump){// If the channel is trying to authenticate to a proxy and// that was canceled we cannot show the http response body// from the 40x as that might mislead the user into thinking// it was a end host response instead of a proxy reponse.// This must check explicitly whether a proxy auth was being done// because we do want to show the content if this is an error from// the origin server.if(mProxyAuthPending)Cancel(NS_ERROR_PROXY_CONNECTION_REFUSED);// ensure call of OnStartRequest of the current listener here,// it would not be called otherwise at allnsresultrv=CallOnStartRequest();// drop mAuthRetryPending flag and resume the transaction// this resumes load of the unauthenticated content data (which// may have been canceled if we don't want to show it)mAuthRetryPending=false;LOG(("Resuming the transaction, user cancelled the auth dialog"));mTransactionPump->Resume();if(NS_FAILED(rv))mTransactionPump->Cancel(rv);}mProxyAuthPending=false;returnNS_OK;}NS_IMETHODIMPnsHttpChannel::CloseStickyConnection(){LOG(("nsHttpChannel::CloseStickyConnection this=%p",this));// Require we are between OnStartRequest and OnStopRequest, because// what we do here takes effect in OnStopRequest (not reusing the// connection for next authentication round).if(!mIsPending){LOG((" channel not pending"));NS_ERROR("CloseStickyConnection not called before OnStopRequest, won't have any effect");returnNS_ERROR_UNEXPECTED;}MOZ_ASSERT(mTransaction);if(!mTransaction){returnNS_ERROR_UNEXPECTED;}if(!(mCaps&NS_HTTP_STICKY_CONNECTION||mTransaction->Caps()&NS_HTTP_STICKY_CONNECTION)){LOG((" not sticky"));returnNS_OK;}RefPtr<nsAHttpConnection>conn=mTransaction->GetConnectionReference();if(!conn){LOG((" no connection"));returnNS_OK;}// This turns the IsPersistent() indicator on the connection to false,// and makes us throw it away in OnStopRequest.conn->DontReuse();returnNS_OK;}NS_IMETHODIMPnsHttpChannel::ConnectionRestartable(boolaRestartable){LOG(("nsHttpChannel::ConnectionRestartable this=%p, restartable=%d",this,aRestartable));mAuthConnectionRestartable=aRestartable;returnNS_OK;}//-----------------------------------------------------------------------------// nsHttpChannel::nsISupports//-----------------------------------------------------------------------------NS_IMPL_ADDREF_INHERITED(nsHttpChannel,HttpBaseChannel)NS_IMPL_RELEASE_INHERITED(nsHttpChannel,HttpBaseChannel)NS_INTERFACE_MAP_BEGIN(nsHttpChannel)NS_INTERFACE_MAP_ENTRY(nsIRequest)NS_INTERFACE_MAP_ENTRY(nsIChannel)NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)NS_INTERFACE_MAP_ENTRY(nsIStreamListener)NS_INTERFACE_MAP_ENTRY(nsIHttpChannel)NS_INTERFACE_MAP_ENTRY(nsICacheInfoChannel)NS_INTERFACE_MAP_ENTRY(nsICachingChannel)NS_INTERFACE_MAP_ENTRY(nsIClassOfService)NS_INTERFACE_MAP_ENTRY(nsIUploadChannel)NS_INTERFACE_MAP_ENTRY(nsIFormPOSTActionChannel)NS_INTERFACE_MAP_ENTRY(nsIUploadChannel2)NS_INTERFACE_MAP_ENTRY(nsICacheEntryOpenCallback)NS_INTERFACE_MAP_ENTRY(nsIHttpChannelInternal)NS_INTERFACE_MAP_ENTRY(nsIResumableChannel)NS_INTERFACE_MAP_ENTRY(nsITransportEventSink)NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyCallback)NS_INTERFACE_MAP_ENTRY(nsIInputAvailableCallback)NS_INTERFACE_MAP_ENTRY(nsIProxiedChannel)NS_INTERFACE_MAP_ENTRY(nsIHttpAuthenticableChannel)NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheContainer)NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheChannel)NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableRequest)NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableStreamListener)NS_INTERFACE_MAP_ENTRY(nsIDNSListener)NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)NS_INTERFACE_MAP_ENTRY(nsICorsPreflightCallback)NS_INTERFACE_MAP_ENTRY(nsIRaceCacheWithNetwork)NS_INTERFACE_MAP_ENTRY(nsITimerCallback)NS_INTERFACE_MAP_ENTRY(nsIHstsPrimingCallback)NS_INTERFACE_MAP_ENTRY(nsIChannelWithDivertableParentListener)// we have no macro that covers this case.if(aIID.Equals(NS_GET_IID(nsHttpChannel))){AddRef();*aInstancePtr=this;returnNS_OK;}elseNS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel)//-----------------------------------------------------------------------------// nsHttpChannel::nsIRequest//-----------------------------------------------------------------------------NS_IMETHODIMPnsHttpChannel::Cancel(nsresultstatus){MOZ_ASSERT(NS_IsMainThread());// We should never have a pump open while a CORS preflight is in progress.MOZ_ASSERT_IF(mPreflightChannel,!mCachePump);LOG(("nsHttpChannel::Cancel [this=%p status=%"PRIx32"]\n",this,static_cast<uint32_t>(status)));if(mCanceled){LOG((" ignoring; already canceled\n"));returnNS_OK;}if(mWaitingForRedirectCallback){LOG(("channel canceled during wait for redirect callback"));}mCanceled=true;mStatus=status;if(mProxyRequest)mProxyRequest->Cancel(status);CancelNetworkRequest(status);mCacheInputStream.CloseAndRelease();if(mCachePump)mCachePump->Cancel(status);if(mAuthProvider)mAuthProvider->Cancel(status);if(mPreflightChannel)mPreflightChannel->Cancel(status);returnNS_OK;}voidnsHttpChannel::CancelNetworkRequest(nsresultaStatus){if(mTransaction){nsresultrv=gHttpHandler->CancelTransaction(mTransaction,aStatus);if(NS_FAILED(rv)){LOG(("failed to cancel the transaction\n"));}}if(mTransactionPump)mTransactionPump->Cancel(aStatus);}NS_IMETHODIMPnsHttpChannel::Suspend(){nsresultrv=SuspendInternal();nsresultrvParentChannel=NS_OK;if(mParentChannel){rvParentChannel=mParentChannel->SuspendMessageDiversion();}returnNS_FAILED(rv)?rv:rvParentChannel;}NS_IMETHODIMPnsHttpChannel::Resume(){nsresultrv=ResumeInternal();nsresultrvParentChannel=NS_OK;if(mParentChannel){rvParentChannel=mParentChannel->ResumeMessageDiversion();}returnNS_FAILED(rv)?rv:rvParentChannel;}//-----------------------------------------------------------------------------// nsHttpChannel::nsIChannel//-----------------------------------------------------------------------------NS_IMETHODIMPnsHttpChannel::GetSecurityInfo(nsISupports**securityInfo){NS_ENSURE_ARG_POINTER(securityInfo);*securityInfo=mSecurityInfo;NS_IF_ADDREF(*securityInfo);returnNS_OK;}// If any of the functions that AsyncOpen calls returns immediately an error// AsyncAbort(which calls onStart/onStopRequest) does not need to be call.// To be sure that they are not call ReleaseListeners() is called.// If AsyncOpen returns NS_OK, after that point AsyncAbort must be called on// any error.NS_IMETHODIMPnsHttpChannel::AsyncOpen(nsIStreamListener*listener,nsISupports*context){MOZ_ASSERT(!mLoadInfo||mLoadInfo->GetSecurityMode()==0||mLoadInfo->GetInitialSecurityCheckDone()||(mLoadInfo->GetSecurityMode()==nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL&&nsContentUtils::IsSystemPrincipal(mLoadInfo->LoadingPrincipal())),"security flags in loadInfo but asyncOpen2() not called");LOG(("nsHttpChannel::AsyncOpen [this=%p]\n",this));#ifdef MOZ_TASK_TRACERif(tasktracer::IsStartLogging()){uint64_tsourceEventId,parentTaskId;tasktracer::SourceEventTypesourceEventType;GetCurTraceInfo(&sourceEventId,&parentTaskId,&sourceEventType);nsCOMPtr<nsIURI>uri;GetURI(getter_AddRefs(uri));nsAutoCStringurispec;uri->GetSpec(urispec);tasktracer::AddLabel("nsHttpChannel::AsyncOpen %s",urispec.get());}#endifNS_CompareLoadInfoAndLoadContext(this);#ifdef DEBUGAssertPrivateBrowsingId();#endifNS_ENSURE_ARG_POINTER(listener);NS_ENSURE_TRUE(!mIsPending,NS_ERROR_IN_PROGRESS);NS_ENSURE_TRUE(!mWasOpened,NS_ERROR_ALREADY_OPENED);nsresultrv;MOZ_ASSERT(NS_IsMainThread());if(!gHttpHandler->Active()){LOG((" after HTTP shutdown..."));ReleaseListeners();returnNS_ERROR_NOT_AVAILABLE;}staticboolsRCWNInited=false;if(!sRCWNInited){sRCWNInited=true;Preferences::AddBoolVarCache(&sRCWNEnabled,"network.http.rcwn.enabled");Preferences::AddUintVarCache(&sRCWNQueueSizeNormal,"network.http.rcwn.cache_queue_normal_threshold");Preferences::AddUintVarCache(&sRCWNQueueSizePriority,"network.http.rcwn.cache_queue_priority_threshold");Preferences::AddUintVarCache(&sRCWNSmallResourceSizeKB,"network.http.rcwn.small_resource_size_kb");}rv=NS_CheckPortSafety(mURI);if(NS_FAILED(rv)){ReleaseListeners();returnrv;}if(mInterceptCache!=INTERCEPTED&&ShouldIntercept()){mInterceptCache=MAYBE_INTERCEPT;SetCouldBeSynthesized();}// Remember the cookie header that was set, if anynsAutoCStringcookieHeader;if(NS_SUCCEEDED(mRequestHead.GetHeader(nsHttp::Cookie,cookieHeader))){mUserSetCookieHeader=cookieHeader;}AddCookiesToRequest();// Set user agent override, do so before OnOpeningRequest notification// since we want to allow consumers of that notification change or remove// the User-Agent request header.HttpBaseChannel::SetDocshellUserAgentOverride();// After we notify any observers (on-opening-request, loadGroup, etc) we// must return NS_OK and return any errors asynchronously via// OnStart/OnStopRequest. Observers may add a reference to the channel// and expect to get OnStopRequest so they know when to drop the reference,// etc.// notify "http-on-opening-request" observers, but not if this is a redirectif(!(mLoadFlags&LOAD_REPLACE)){gHttpHandler->OnOpeningRequest(this);}mIsPending=true;mWasOpened=true;mListener=listener;mListenerContext=context;if(mLoadGroup)mLoadGroup->AddRequest(this,nullptr);// record asyncopen time unconditionally and clear it if we// don't want it after OnModifyRequest() weighs in. But waiting for// that to complete would mean we don't include proxy resolution in the// timing.mAsyncOpenTime=TimeStamp::Now();// Remember we have Authorization header set here. We need to check on it// just once and early, AsyncOpen is the best place.mCustomAuthHeader=mRequestHead.HasHeader(nsHttp::Authorization);// The common case for HTTP channels is to begin proxy resolution and return// at this point. The only time we know mProxyInfo already is if we're// proxying a non-http protocol like ftp.if(!mProxyInfo&&NS_SUCCEEDED(ResolveProxy())){returnNS_OK;}rv=BeginConnect();if(NS_FAILED(rv)){CloseCacheEntry(false);Unused<<AsyncAbort(rv);}returnNS_OK;}namespace{classInitLocalBlockListXpcCallbackfinal:publicnsIURIClassifierCallback{public:usingCallbackType=nsHttpChannel::InitLocalBlockListCallback;explicitInitLocalBlockListXpcCallback(constCallbackType&aCallback):mCallback(aCallback){}NS_DECL_ISUPPORTSNS_DECL_NSIURICLASSIFIERCALLBACKprivate:~InitLocalBlockListXpcCallback()=default;CallbackTypemCallback;};NS_IMPL_ISUPPORTS(InitLocalBlockListXpcCallback,nsIURIClassifierCallback)/*virtual*/nsresultInitLocalBlockListXpcCallback::OnClassifyComplete(nsresultaErrorCode,// Only this matters.constnsACString&/*aLists*/,constnsACString&/*aProvider*/,constnsACString&/*aPrefix*/){boollocalBlockList=aErrorCode==NS_ERROR_TRACKING_URI;mCallback(localBlockList);returnNS_OK;}}// end of unnamed namespace/already_AddRefed<nsChannelClassifier>nsHttpChannel::GetOrCreateChannelClassifier(){if(!mChannelClassifier){mChannelClassifier=newnsChannelClassifier(this);LOG(("nsHttpChannel [%p] created nsChannelClassifier [%p]\n",this,mChannelClassifier.get()));}RefPtr<nsChannelClassifier>classifier=mChannelClassifier;returnclassifier.forget();}boolnsHttpChannel::InitLocalBlockList(constInitLocalBlockListCallback&aCallback){mLocalBlocklist=false;if(!(mLoadFlags&LOAD_CLASSIFY_URI)){returnfalse;}// Check to see if this principal exists on local blocklists.RefPtr<nsChannelClassifier>channelClassifier=GetOrCreateChannelClassifier();// We skip speculative connections by setting mLocalBlocklist only// when tracking protection is enabled. Though we could do this for// both phishing and malware, it is not necessary for correctness,// since no network events will be received while the// nsChannelClassifier is in progress. See bug 1122691.RefPtr<InitLocalBlockListXpcCallback>xpcCallback=newInitLocalBlockListXpcCallback(aCallback);if(NS_FAILED(channelClassifier->CheckIsTrackerWithLocalTable(xpcCallback))){returnfalse;}returntrue;}NS_IMETHODIMPnsHttpChannel::AsyncOpen2(nsIStreamListener*aListener){nsCOMPtr<nsIStreamListener>listener=aListener;nsresultrv=nsContentSecurityManager::doContentSecurityCheck(this,listener);if(NS_WARN_IF(NS_FAILED(rv))){ReleaseListeners();returnrv;}returnAsyncOpen(listener,nullptr);}// BeginConnect() SHOULD NOT call AsyncAbort(). AsyncAbort will be called by// functions that called BeginConnect if needed. Only AsyncOpen and// OnProxyAvailable ever call BeginConnect.nsresultnsHttpChannel::BeginConnect(){LOG(("nsHttpChannel::BeginConnect [this=%p]\n",this));nsresultrv;// Construct connection info objectnsAutoCStringhost;nsAutoCStringscheme;int32_tport=-1;boolisHttps=false;rv=mURI->GetScheme(scheme);if(NS_SUCCEEDED(rv))rv=mURI->SchemeIs("https",&isHttps);if(NS_SUCCEEDED(rv))rv=mURI->GetAsciiHost(host);if(NS_SUCCEEDED(rv))rv=mURI->GetPort(&port);if(NS_SUCCEEDED(rv))mURI->GetUsername(mUsername);if(NS_SUCCEEDED(rv))rv=mURI->GetAsciiSpec(mSpec);if(NS_FAILED(rv)){returnrv;}// Reject the URL if it doesn't specify a hostif(host.IsEmpty()){rv=NS_ERROR_MALFORMED_URI;returnrv;}LOG(("host=%s port=%d\n",host.get(),port));LOG(("uri=%s\n",mSpec.get()));nsCOMPtr<nsProxyInfo>proxyInfo;if(mProxyInfo)proxyInfo=do_QueryInterface(mProxyInfo);mRequestHead.SetHTTPS(isHttps);mRequestHead.SetOrigin(scheme,host,port);SetDoNotTrack();OriginAttributesoriginAttributes;NS_GetOriginAttributes(this,originAttributes);RefPtr<AltSvcMapping>mapping;if(!mConnectionInfo&&mAllowAltSvc&&// per channel!(mLoadFlags&LOAD_FRESH_CONNECTION)&&(scheme.Equals(NS_LITERAL_CSTRING("http"))||scheme.Equals(NS_LITERAL_CSTRING("https")))&&(!proxyInfo||proxyInfo->IsDirect())&&(mapping=gHttpHandler->GetAltServiceMapping(scheme,host,port,mPrivateBrowsing,originAttributes))){LOG(("nsHttpChannel %p Alt Service Mapping Found %s://%s:%d [%s]\n",this,scheme.get(),mapping->AlternateHost().get(),mapping->AlternatePort(),mapping->HashKey().get()));if(!(mLoadFlags&LOAD_ANONYMOUS)&&!mPrivateBrowsing){nsAutoCStringaltUsedLine(mapping->AlternateHost());booldefaultPort=mapping->AlternatePort()==(isHttps?NS_HTTPS_DEFAULT_PORT:NS_HTTP_DEFAULT_PORT);if(!defaultPort){altUsedLine.AppendLiteral(":");altUsedLine.AppendInt(mapping->AlternatePort());}rv=mRequestHead.SetHeader(nsHttp::Alternate_Service_Used,altUsedLine);MOZ_ASSERT(NS_SUCCEEDED(rv));}nsCOMPtr<nsIConsoleService>consoleService=do_GetService(NS_CONSOLESERVICE_CONTRACTID);if(consoleService){nsAutoStringmessage(NS_LITERAL_STRING("Alternate Service Mapping found: "));AppendASCIItoUTF16(scheme.get(),message);message.Append(NS_LITERAL_STRING("://"));AppendASCIItoUTF16(host.get(),message);message.Append(NS_LITERAL_STRING(":"));message.AppendInt(port);message.Append(NS_LITERAL_STRING(" to "));AppendASCIItoUTF16(scheme.get(),message);message.Append(NS_LITERAL_STRING("://"));AppendASCIItoUTF16(mapping->AlternateHost().get(),message);message.Append(NS_LITERAL_STRING(":"));message.AppendInt(mapping->AlternatePort());consoleService->LogStringMessage(message.get());}LOG(("nsHttpChannel %p Using connection info from altsvc mapping",this));mapping->GetConnectionInfo(getter_AddRefs(mConnectionInfo),proxyInfo,originAttributes);Telemetry::Accumulate(Telemetry::HTTP_TRANSACTION_USE_ALTSVC,true);Telemetry::Accumulate(Telemetry::HTTP_TRANSACTION_USE_ALTSVC_OE,!isHttps);}elseif(mConnectionInfo){LOG(("nsHttpChannel %p Using channel supplied connection info",this));Telemetry::Accumulate(Telemetry::HTTP_TRANSACTION_USE_ALTSVC,false);}else{LOG(("nsHttpChannel %p Using default connection info",this));mConnectionInfo=newnsHttpConnectionInfo(host,port,EmptyCString(),mUsername,proxyInfo,originAttributes,isHttps);Telemetry::Accumulate(Telemetry::HTTP_TRANSACTION_USE_ALTSVC,false);}// Set network interface id only when it's not empty to avoid// rebuilding hash key.if(!mNetworkInterfaceId.IsEmpty()){mConnectionInfo->SetNetworkInterfaceId(mNetworkInterfaceId);}mAuthProvider=do_CreateInstance("@mozilla.org/network/http-channel-auth-provider;1",&rv);if(NS_SUCCEEDED(rv))rv=mAuthProvider->Init(this);if(NS_FAILED(rv)){returnrv;}// check to see if authorization headers should be included// mCustomAuthHeader is set in AsyncOpen if we find Authorization headerrv=mAuthProvider->AddAuthorizationHeaders(mCustomAuthHeader);if(NS_FAILED(rv)){LOG(("nsHttpChannel %p AddAuthorizationHeaders failed (%08x)",this,static_cast<uint32_t>(rv)));}// notify "http-on-modify-request" observersCallOnModifyRequestObservers();SetLoadGroupUserAgentOverride();// Check if request was cancelled during on-modify-request or on-useragent.if(mCanceled){returnmStatus;}if(mSuspendCount){LOG(("Waiting until resume BeginConnect [this=%p]\n",this));MOZ_ASSERT(!mCallOnResume);mCallOnResume=&nsHttpChannel::HandleBeginConnectContinue;returnNS_OK;}returnBeginConnectContinue();}voidnsHttpChannel::HandleBeginConnectContinue(){NS_PRECONDITION(!mCallOnResume,"How did that happen?");nsresultrv;if(mSuspendCount){LOG(("Waiting until resume BeginConnect [this=%p]\n",this));mCallOnResume=&nsHttpChannel::HandleBeginConnectContinue;return;}LOG(("nsHttpChannel::HandleBeginConnectContinue [this=%p]\n",this));rv=BeginConnectContinue();if(NS_FAILED(rv)){CloseCacheEntry(false);Unused<<AsyncAbort(rv);}}nsresultnsHttpChannel::BeginConnectContinue(){nsresultrv;// Check if request was cancelled during suspend AFTER on-modify-request or// on-useragent.if(mCanceled){returnmStatus;}// Check to see if we should redirect this channel elsewhere by// nsIHttpChannel.redirectTo API requestif(mAPIRedirectToURI){returnAsyncCall(&nsHttpChannel::HandleAsyncAPIRedirect);}// If mTimingEnabled flag is not set after OnModifyRequest() then// clear the already recorded AsyncOpen value for consistency.if(!mTimingEnabled)mAsyncOpenTime=TimeStamp();// if this somehow fails we can go on without itUnused<<gHttpHandler->AddConnectionHeader(&mRequestHead,mCaps);if(mLoadFlags&VALIDATE_ALWAYS||BYPASS_LOCAL_CACHE(mLoadFlags))mCaps|=NS_HTTP_REFRESH_DNS;// Adjust mCaps according to our request headers:// - If "Connection: close" is set as a request header, then do not bother// trying to establish a keep-alive connection.if(mRequestHead.HasHeaderValue(nsHttp::Connection,"close"))mCaps&=~(NS_HTTP_ALLOW_KEEPALIVE);if(gHttpHandler->CriticalRequestPrioritization()){if(mClassOfService&nsIClassOfService::Leader){mCaps|=NS_HTTP_LOAD_AS_BLOCKING;}if(mClassOfService&nsIClassOfService::Unblocked){mCaps|=NS_HTTP_LOAD_UNBLOCKED;}if(mClassOfService&nsIClassOfService::UrgentStart){mCaps|=NS_HTTP_URGENT_START;SetPriority(nsISupportsPriority::PRIORITY_HIGHEST);}}// Force-Reload should reset the persistent connection pool for this hostif(mLoadFlags&LOAD_FRESH_CONNECTION){// just the initial document resets the whole poolif(mLoadFlags&LOAD_INITIAL_DOCUMENT_URI){gHttpHandler->ConnMgr()->ClearAltServiceMappings();rv=gHttpHandler->ConnMgr()->DoShiftReloadConnectionCleanup(mConnectionInfo);if(NS_FAILED(rv)){LOG(("nsHttpChannel::BeginConnect ""DoShiftReloadConnectionCleanup failed: %08x [this=%p]",static_cast<uint32_t>(rv),this));}}}// We may have been cancelled already, either by on-modify-request// listeners or load group observers; in that case, we should not send the// request to the serverif(mCanceled){returnmStatus;}if(!(mLoadFlags&LOAD_CLASSIFY_URI)){returnContinueBeginConnectWithResult();}// We are about to do a async lookup to check if the URI is a// tracker. The result will be delivered along with the callback.// Chances are the lookup is not needed so InitLocalBlockList()// will return false and then we can BeginConnectActual() right away.RefPtr<nsHttpChannel>self=this;boolwillCallback=InitLocalBlockList([self](boolaLocalBlockList)->void{self->mLocalBlocklist=aLocalBlockList;nsresultrv=self->BeginConnectActual();if(NS_FAILED(rv)){// Since this error is thrown asynchronously so that the caller// of BeginConnect() will not do clean up for us. We have to do// it on our own.self->CloseCacheEntry(false);Unused<<self->AsyncAbort(rv);}});if(!willCallback){// We can do BeginConnectActual immediately if mLocalBlockList is initialized// synchronously. Note that we don't need to handle the failure because// BeginConnect() will return synchronously and the caller will be responsible// for handling it.returnBeginConnectActual();}returnNS_OK;}nsresultnsHttpChannel::BeginConnectActual(){if(mCanceled){returnmStatus;}if(!mLocalBlocklist&&!mConnectionInfo->UsingHttpProxy()&&!(mLoadFlags&(LOAD_NO_NETWORK_IO|LOAD_ONLY_FROM_CACHE))){// Start a DNS lookup very early in case the real open is queued the DNS can// happen in parallel. Do not do so in the presence of an HTTP proxy as// all lookups other than for the proxy itself are done by the proxy.// Also we don't do a lookup if the LOAD_NO_NETWORK_IO or// LOAD_ONLY_FROM_CACHE flags are set.//// We keep the DNS prefetch object around so that we can retrieve// timing information from it. There is no guarantee that we actually// use the DNS prefetch data for the real connection, but as we keep// this data around for 3 minutes by default, this should almost always// be correct, and even when it isn't, the timing still represents _a_// valid DNS lookup timing for the site, even if it is not _the_// timing we used.LOG(("nsHttpChannel::BeginConnect [this=%p] prefetching%s\n",this,mCaps&NS_HTTP_REFRESH_DNS?", refresh requested":""));OriginAttributesoriginAttributes;NS_GetOriginAttributes(this,originAttributes);mDNSPrefetch=newnsDNSPrefetch(mURI,originAttributes,this,mTimingEnabled);mDNSPrefetch->PrefetchHigh(mCaps&NS_HTTP_REFRESH_DNS);}// mLocalBlocklist is true only if tracking protection is enabled and the// URI is a tracking domain, it makes no guarantees about phishing or// malware, so if LOAD_CLASSIFY_URI is true we must call// nsChannelClassifier to catch phishing and malware URIs.boolcallContinueBeginConnect=true;if(!mLocalBlocklist){// Here we call ContinueBeginConnectWithResult and not// ContinueBeginConnect so that in the case of an error we do not start// channelClassifier.nsresultrv=ContinueBeginConnectWithResult();if(NS_FAILED(rv)){returnrv;}callContinueBeginConnect=false;}// nsChannelClassifier calls ContinueBeginConnect if it has not already// been called, after optionally cancelling the channel once we have a// remote verdict. We call a concrete class instead of an nsI* that might// be overridden.RefPtr<nsChannelClassifier>channelClassifier=GetOrCreateChannelClassifier();LOG(("nsHttpChannel::Starting nsChannelClassifier %p [this=%p]",channelClassifier.get(),this));channelClassifier->Start();if(callContinueBeginConnect){returnContinueBeginConnectWithResult();}returnNS_OK;}NS_IMETHODIMPnsHttpChannel::GetEncodedBodySize(uint64_t*aEncodedBodySize){if(mCacheEntry&&!mCacheEntryIsWriteOnly){int64_tdataSize=0;mCacheEntry->GetDataSize(&dataSize);*aEncodedBodySize=dataSize;}else{*aEncodedBodySize=mLogicalOffset;}returnNS_OK;}//-----------------------------------------------------------------------------// nsHttpChannel::nsIHttpChannelInternal//-----------------------------------------------------------------------------NS_IMETHODIMPnsHttpChannel::SetupFallbackChannel(constchar*aFallbackKey){ENSURE_CALLED_BEFORE_CONNECT();LOG(("nsHttpChannel::SetupFallbackChannel [this=%p, key=%s]\n",this,aFallbackKey));mFallbackChannel=true;mFallbackKey=aFallbackKey;returnNS_OK;}NS_IMETHODIMPnsHttpChannel::ForceIntercepted(uint64_taInterceptionID){ENSURE_CALLED_BEFORE_ASYNC_OPEN();if(NS_WARN_IF(mLoadFlags&LOAD_BYPASS_SERVICE_WORKER)){returnNS_ERROR_NOT_AVAILABLE;}MarkIntercepted();mResponseCouldBeSynthesized=true;mInterceptionID=aInterceptionID;returnNS_OK;}NS_IMETHODIMPnsHttpChannel::SetChannelIsForDownload(boolaChannelIsForDownload){if(aChannelIsForDownload){AddClassFlags(nsIClassOfService::Throttleable);}else{ClearClassFlags(nsIClassOfService::Throttleable);}returnHttpBaseChannel::SetChannelIsForDownload(aChannelIsForDownload);}//-----------------------------------------------------------------------------// nsHttpChannel::nsISupportsPriority//-----------------------------------------------------------------------------NS_IMETHODIMPnsHttpChannel::SetPriority(int32_tvalue){int16_tnewValue=clamped<int32_t>(value,INT16_MIN,INT16_MAX);if(mPriority==newValue)returnNS_OK;mPriority=newValue;if(mTransaction){nsresultrv=gHttpHandler->RescheduleTransaction(mTransaction,mPriority);if(NS_FAILED(rv)){LOG(("nsHttpChannel::SetPriority [this=%p] ""RescheduleTransaction failed (%08x)",this,static_cast<uint32_t>(rv)));}}// If this channel is the real channel for an e10s channel, notify the// child side about the priority change as well.nsCOMPtr<nsIParentChannel>parentChannel;NS_QueryNotificationCallbacks(this,parentChannel);RefPtr<HttpChannelParent>httpParent=do_QueryObject(parentChannel);if(httpParent){httpParent->DoSendSetPriority(newValue);}returnNS_OK;}nsresultnsHttpChannel::ContinueBeginConnectWithResult(){LOG(("nsHttpChannel::ContinueBeginConnectWithResult [this=%p]",this));NS_PRECONDITION(!mCallOnResume,"How did that happen?");nsresultrv;if(mSuspendCount){LOG(("Waiting until resume to do async connect [this=%p]\n",this));mCallOnResume=&nsHttpChannel::ContinueBeginConnect;rv=NS_OK;}elseif(mCanceled){// We may have been cancelled already, by nsChannelClassifier in that// case, we should not send the request to the serverrv=mStatus;}else{rv=Connect();}LOG(("nsHttpChannel::ContinueBeginConnectWithResult result [this=%p rv=%"PRIx32" mCanceled=%u]\n",this,static_cast<uint32_t>(rv),static_cast<bool>(mCanceled)));returnrv;}voidnsHttpChannel::ContinueBeginConnect(){nsresultrv=ContinueBeginConnectWithResult();if(NS_FAILED(rv)){CloseCacheEntry(false);Unused<<AsyncAbort(rv);}}//-----------------------------------------------------------------------------// HttpChannel::nsIClassOfService//-----------------------------------------------------------------------------voidnsHttpChannel::OnClassOfServiceUpdated(){if(mTransaction){gHttpHandler->UpdateClassOfServiceOnTransaction(mTransaction,mClassOfService);}}NS_IMETHODIMPnsHttpChannel::SetClassFlags(uint32_tinFlags){uint32_tprevious=mClassOfService;mClassOfService=inFlags;if(previous!=mClassOfService){OnClassOfServiceUpdated();}returnNS_OK;}NS_IMETHODIMPnsHttpChannel::AddClassFlags(uint32_tinFlags){uint32_tprevious=mClassOfService;mClassOfService|=inFlags;if(previous!=mClassOfService){OnClassOfServiceUpdated();}returnNS_OK;}NS_IMETHODIMPnsHttpChannel::ClearClassFlags(uint32_tinFlags){uint32_tprevious=mClassOfService;mClassOfService&=~inFlags;if(previous!=mClassOfService){OnClassOfServiceUpdated();}returnNS_OK;}//-----------------------------------------------------------------------------// nsHttpChannel::nsIProtocolProxyCallback//-----------------------------------------------------------------------------NS_IMETHODIMPnsHttpChannel::OnProxyAvailable(nsICancelable*request,nsIChannel*channel,nsIProxyInfo*pi,nsresultstatus){LOG(("nsHttpChannel::OnProxyAvailable [this=%p pi=%p status=%"PRIx32" mStatus=%"PRIx32"]\n",this,pi,static_cast<uint32_t>(status),static_cast<uint32_t>(static_cast<nsresult>(mStatus))));mProxyRequest=nullptr;nsresultrv;// If status is a failure code, then it means that we failed to resolve// proxy info. That is a non-fatal error assuming it wasn't because the// request was canceled. We just failover to DIRECT when proxy resolution// fails (failure can mean that the PAC URL could not be loaded).if(NS_SUCCEEDED(status))mProxyInfo=pi;if(!gHttpHandler->Active()){LOG(("nsHttpChannel::OnProxyAvailable [this=%p] ""Handler no longer active.\n",this));rv=NS_ERROR_NOT_AVAILABLE;}else{rv=BeginConnect();}if(NS_FAILED(rv)){CloseCacheEntry(false);Unused<<AsyncAbort(rv);}returnrv;}//-----------------------------------------------------------------------------// nsHttpChannel::nsIProxiedChannel//-----------------------------------------------------------------------------NS_IMETHODIMPnsHttpChannel::GetProxyInfo(nsIProxyInfo**result){if(!mConnectionInfo)*result=mProxyInfo;else*result=mConnectionInfo->ProxyInfo();NS_IF_ADDREF(*result);returnNS_OK;}//-----------------------------------------------------------------------------// nsHttpChannel::nsITimedChannel//-----------------------------------------------------------------------------NS_IMETHODIMPnsHttpChannel::GetDomainLookupStart(TimeStamp*_retval){if(mTransaction)*_retval=mTransaction->GetDomainLookupStart();else*_retval=mTransactionTimings.domainLookupStart;returnNS_OK;}NS_IMETHODIMPnsHttpChannel::GetDomainLookupEnd(TimeStamp*_retval){if(mTransaction)*_retval=mTransaction->GetDomainLookupEnd();else*_retval=mTransactionTimings.domainLookupEnd;returnNS_OK;}NS_IMETHODIMPnsHttpChannel::GetConnectStart(TimeStamp*_retval){if(mTransaction)*_retval=mTransaction->GetConnectStart();else*_retval=mTransactionTimings.connectStart;returnNS_OK;}NS_IMETHODIMPnsHttpChannel::GetConnectEnd(TimeStamp*_retval){if(mTransaction)*_retval=mTransaction->GetConnectEnd();else*_retval=mTransactionTimings.connectEnd;returnNS_OK;}NS_IMETHODIMPnsHttpChannel::GetRequestStart(TimeStamp*_retval){if(mTransaction)*_retval=mTransaction->GetRequestStart();else*_retval=mTransactionTimings.requestStart;returnNS_OK;}NS_IMETHODIMPnsHttpChannel::GetResponseStart(TimeStamp*_retval){if(mTransaction)*_retval=mTransaction->GetResponseStart();else*_retval=mTransactionTimings.responseStart;returnNS_OK;}NS_IMETHODIMPnsHttpChannel::GetResponseEnd(TimeStamp*_retval){if(mTransaction)*_retval=mTransaction->GetResponseEnd();else*_retval=mTransactionTimings.responseEnd;returnNS_OK;}//-----------------------------------------------------------------------------// nsHttpChannel::nsIHttpAuthenticableChannel//-----------------------------------------------------------------------------NS_IMETHODIMPnsHttpChannel::GetIsSSL(bool*aIsSSL){// this attribute is really misnamed - it wants to know if// https:// is being used. SSL might be used to cover http://// in some circumstances (proxies, http/2, etc..)returnmURI->SchemeIs("https",aIsSSL);}NS_IMETHODIMPnsHttpChannel::GetProxyMethodIsConnect(bool*aProxyMethodIsConnect){*aProxyMethodIsConnect=mConnectionInfo->UsingConnect();returnNS_OK;}NS_IMETHODIMPnsHttpChannel::GetServerResponseHeader(nsACString&value){if(!mResponseHead)returnNS_ERROR_NOT_AVAILABLE;returnmResponseHead->GetHeader(nsHttp::Server,value);}NS_IMETHODIMPnsHttpChannel::GetProxyChallenges(nsACString&value){if(!mResponseHead)returnNS_ERROR_UNEXPECTED;returnmResponseHead->GetHeader(nsHttp::Proxy_Authenticate,value);}NS_IMETHODIMPnsHttpChannel::GetWWWChallenges(nsACString&value){if(!mResponseHead)returnNS_ERROR_UNEXPECTED;returnmResponseHead->GetHeader(nsHttp::WWW_Authenticate,value);}NS_IMETHODIMPnsHttpChannel::SetProxyCredentials(constnsACString&value){returnmRequestHead.SetHeader(nsHttp::Proxy_Authorization,value);}NS_IMETHODIMPnsHttpChannel::SetWWWCredentials(constnsACString&value){// This method is called when various browser initiated authorization// code sets the credentials. We need to flag this header as the// "browser default" so it does not show up in the ServiceWorker// FetchEvent. This may actually get called more than once, though,// so we clear the header first since "default" headers are not// allowed to overwrite normally.Unused<<mRequestHead.ClearHeader(nsHttp::Authorization);returnmRequestHead.SetHeader(nsHttp::Authorization,value,false,nsHttpHeaderArray::eVarietyRequestDefault);}//-----------------------------------------------------------------------------// Methods that nsIHttpAuthenticableChannel dupes from other IDLs, which we// get from HttpBaseChannel, must be explicitly forwarded, because C++ sucks.//NS_IMETHODIMPnsHttpChannel::GetLoadFlags(nsLoadFlags*aLoadFlags){returnHttpBaseChannel::GetLoadFlags(aLoadFlags);}NS_IMETHODIMPnsHttpChannel::GetURI(nsIURI**aURI){returnHttpBaseChannel::GetURI(aURI);}NS_IMETHODIMPnsHttpChannel::GetNotificationCallbacks(nsIInterfaceRequestor**aCallbacks){returnHttpBaseChannel::GetNotificationCallbacks(aCallbacks);}NS_IMETHODIMPnsHttpChannel::GetLoadGroup(nsILoadGroup**aLoadGroup){returnHttpBaseChannel::GetLoadGroup(aLoadGroup);}NS_IMETHODIMPnsHttpChannel::GetRequestMethod(nsACString&aMethod){returnHttpBaseChannel::GetRequestMethod(aMethod);}//-----------------------------------------------------------------------------// nsHttpChannel::nsIRequestObserver//-----------------------------------------------------------------------------NS_IMETHODIMPnsHttpChannel::OnStartRequest(nsIRequest*request,nsISupports*ctxt){nsresultrv;AUTO_PROFILER_LABEL("nsHttpChannel::OnStartRequest",NETWORK);if(!(mCanceled||NS_FAILED(mStatus))&&!WRONG_RACING_RESPONSE_SOURCE(request)){// capture the request's status, so our consumers will know ASAP of any// connection failures, etc - bug 93581nsresultstatus;request->GetStatus(&status);mStatus=status;}LOG(("nsHttpChannel::OnStartRequest [this=%p request=%p status=%"PRIx32"]\n",this,request,static_cast<uint32_t>(static_cast<nsresult>(mStatus))));if(mRaceCacheWithNetwork){LOG((" racingNetAndCache - mFirstResponseSource:%d fromCache:%d fromNet:%d\n",mFirstResponseSource,request==mCachePump,request==mTransactionPump));if(mFirstResponseSource==RESPONSE_PENDING){// When the cache wins mFirstResponseSource is set to RESPONSE_FROM_CACHE// earlier in ReadFromCache, so this must be a response from the network.MOZ_ASSERT(request==mTransactionPump);LOG((" First response from network\n"));mFirstResponseSource=RESPONSE_FROM_NETWORK;}elseif(WRONG_RACING_RESPONSE_SOURCE(request)){LOG((" Early return when racing. This response not needed."));returnNS_OK;}}// Make sure things are what we expect them to be...MOZ_ASSERT(request==mCachePump||request==mTransactionPump,"Unexpected request");MOZ_ASSERT(mRaceCacheWithNetwork||!(mTransactionPump&&mCachePump)||mCachedContentIsPartial,"If we have both pumps, the cache content must be partial");mAfterOnStartRequestBegun=true;mOnStartRequestTimestamp=TimeStamp::Now();Telemetry::Accumulate(Telemetry::HTTP_ONSTART_SUSPEND_TOTAL_TIME,mSuspendTotalTime);if(!mSecurityInfo&&!mCachePump&&mTransaction){// grab the security info from the connection object; the transaction// is guaranteed to own a reference to the connection.mSecurityInfo=mTransaction->SecurityInfo();}// don't enter this block if we're reading from the cache...if(NS_SUCCEEDED(mStatus)&&!mCachePump&&mTransaction){// mTransactionPump doesn't hit OnInputStreamReady and call this until// all of the response headers have been acquired, so we can take ownership// of them from the transaction.mResponseHead=mTransaction->TakeResponseHead();// the response head may be null if the transaction was cancelled. in// which case we just need to call OnStartRequest/OnStopRequest.if(mResponseHead)returnProcessResponse();NS_WARNING("No response head in OnStartRequest");}// cache file could be deleted on our behalf, it could contain errors or// it failed to allocate memory, reload from network here.if(mCacheEntry&&mCachePump&&RECOVER_FROM_CACHE_FILE_ERROR(mStatus)){LOG((" cache file error, reloading from server"));mCacheEntry->AsyncDoom(nullptr);rv=StartRedirectChannelToURI(mURI,nsIChannelEventSink::REDIRECT_INTERNAL);if(NS_SUCCEEDED(rv))returnNS_OK;}// avoid crashing if mListener happens to be null...if(!mListener){NS_NOTREACHED("mListener is null");returnNS_OK;}// before we start any content load, check for redirectTo being called// this code is executed mainly before we start load from the cacheif(mAPIRedirectToURI&&!mCanceled){nsAutoCStringredirectToSpec;mAPIRedirectToURI->GetAsciiSpec(redirectToSpec);LOG((" redirectTo called with uri=%s",redirectToSpec.BeginReading()));MOZ_ASSERT(!mOnStartRequestCalled);nsCOMPtr<nsIURI>redirectTo;mAPIRedirectToURI.swap(redirectTo);PushRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest1);rv=StartRedirectChannelToURI(redirectTo,nsIChannelEventSink::REDIRECT_TEMPORARY);if(NS_SUCCEEDED(rv)){returnNS_OK;}PopRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest1);}// Hack: ContinueOnStartRequest1 uses NS_OK to detect successful redirects,// so we distinguish this codepath (a non-redirect that's processing// normally) by passing in a bogus error code.returnContinueOnStartRequest1(NS_BINDING_FAILED);}nsresultnsHttpChannel::ContinueOnStartRequest1(nsresultresult){if(NS_SUCCEEDED(result)){// Redirect has passed through, we don't want to go on with this// channel. It will now be canceled by the redirect handling code// that called this function.returnNS_OK;}// on proxy errors, try to failoverif(mConnectionInfo->ProxyInfo()&&(mStatus==NS_ERROR_PROXY_CONNECTION_REFUSED||mStatus==NS_ERROR_UNKNOWN_PROXY_HOST||mStatus==NS_ERROR_NET_TIMEOUT)){PushRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest2);if(NS_SUCCEEDED(ProxyFailover()))returnNS_OK;PopRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest2);}// Hack: ContinueOnStartRequest2 uses NS_OK to detect successful redirects,// so we distinguish this codepath (a non-redirect that's processing// normally) by passing in a bogus error code.returnContinueOnStartRequest2(NS_BINDING_FAILED);}nsresultnsHttpChannel::ContinueOnStartRequest2(nsresultresult){if(NS_SUCCEEDED(result)){// Redirect has passed through, we don't want to go on with this// channel. It will now be canceled by the redirect handling code// that called this function.returnNS_OK;}// on other request errors, try to fall backif(NS_FAILED(mStatus)){PushRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest3);boolwaitingForRedirectCallback;Unused<<ProcessFallback(&waitingForRedirectCallback);if(waitingForRedirectCallback)returnNS_OK;PopRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest3);}returnContinueOnStartRequest3(NS_OK);}nsresultnsHttpChannel::ContinueOnStartRequest3(nsresultresult){LOG(("nsHttpChannel::ContinueOnStartRequest3 [this=%p]",this));if(mFallingBack)returnNS_OK;returnCallOnStartRequest();}NS_IMETHODIMPnsHttpChannel::OnStopRequest(nsIRequest*request,nsISupports*ctxt,nsresultstatus){AUTO_PROFILER_LABEL("nsHttpChannel::OnStopRequest",NETWORK);LOG(("nsHttpChannel::OnStopRequest [this=%p request=%p status=%"PRIx32"]\n",this,request,static_cast<uint32_t>(status)));LOG(("OnStopRequest %p requestFromCache: %d mFirstResponseSource: %d\n",this,request==mCachePump,mFirstResponseSource));MOZ_ASSERT(NS_IsMainThread(),"OnStopRequest should only be called from the main thread");if(WRONG_RACING_RESPONSE_SOURCE(request)){returnNS_OK;}if(NS_FAILED(status)){ProcessSecurityReport(status);}// If this load failed because of a security error, it may be because we// are in a captive portal - trigger an async check to make sure.int32_tnsprError=-1*NS_ERROR_GET_CODE(status);if(mozilla::psm::IsNSSErrorCode(nsprError)){gIOService->RecheckCaptivePortal();}if(mTimingEnabled&&request==mCachePump){mCacheReadEnd=TimeStamp::Now();ReportNetVSCacheTelemetry();}// allow content to be cached if it was loaded successfully (bug #482935)boolcontentComplete=NS_SUCCEEDED(status);// honor the cancelation status even if the underlying transaction completed.if(mCanceled||NS_FAILED(mStatus))status=mStatus;if(mCachedContentIsPartial){if(NS_SUCCEEDED(status)){// mTransactionPump should be suspendedMOZ_ASSERT(request!=mTransactionPump,"byte-range transaction finished prematurely");if(request==mCachePump){boolstreamDone;status=OnDoneReadingPartialCacheEntry(&streamDone);if(NS_SUCCEEDED(status)&&!streamDone)returnstatus;// otherwise, fall through and fire OnStopRequest...}elseif(request==mTransactionPump){MOZ_ASSERT(mConcurrentCacheAccess);}elseNS_NOTREACHED("unexpected request");}// Do not to leave the transaction in a suspended state in error cases.if(NS_FAILED(status)&&mTransaction){nsresultrv=gHttpHandler->CancelTransaction(mTransaction,status);if(NS_FAILED(rv)){LOG((" CancelTransaction failed (%08x)",static_cast<uint32_t>(rv)));}}}nsCOMPtr<nsICompressConvStats>conv=do_QueryInterface(mCompressListener);if(conv){conv->GetDecodedDataLength(&mDecodedBodySize);}boolisFromNet=request==mTransactionPump;if(mTransaction){// determine if we should call DoAuthRetryboolauthRetry=mAuthRetryPending&&NS_SUCCEEDED(status);mStronglyFramed=mTransaction->ResponseIsComplete();LOG(("nsHttpChannel %p has a strongly framed transaction: %d",this,mStronglyFramed));//// grab references to connection in case we need to retry an// authentication request over it or use it for an upgrade// to another protocol.//// this code relies on the code in nsHttpTransaction::Close, which// tests for NS_HTTP_STICKY_CONNECTION to determine whether or not to// keep the connection around after the transaction is finished.//RefPtr<nsAHttpConnection>conn;LOG((" authRetry=%d, sticky conn cap=%d",authRetry,mCaps&NS_HTTP_STICKY_CONNECTION));// We must check caps for stickinness also on the transaction because it// might have been updated by the transaction itself during inspection of// the reposnse headers yet on the socket thread (found connection based// auth schema).if(authRetry&&(mCaps&NS_HTTP_STICKY_CONNECTION||mTransaction->Caps()&NS_HTTP_STICKY_CONNECTION)){conn=mTransaction->GetConnectionReference();LOG((" transaction %p provides connection %p",mTransaction.get(),conn.get()));// This is so far a workaround to fix leak when reusing unpersistent// connection for authentication retry. See bug 459620 comment 4// for details.if(conn&&!conn->IsPersistent()){LOG((" connection is not persistent, not reusing it"));conn=nullptr;}}RefPtr<nsAHttpConnection>stickyConn;if(mCaps&NS_HTTP_STICKY_CONNECTION){stickyConn=mTransaction->GetConnectionReference();}mTransferSize=mTransaction->GetTransferSize();// If we are using the transaction to serve content, we also save the// time since async open in the cache entry so we can compare telemetry// between cache and net response.// Do not store the time of conditional requests because even if we// fetch the data from the server, the time includes loading of the old// cache entry which would skew the network load time.if(request==mTransactionPump&&mCacheEntry&&!mDidReval&&!mCustomConditionalRequest&&!mAsyncOpenTime.IsNull()&&!mOnStartRequestTimestamp.IsNull()){uint64_tonStartTime=(mOnStartRequestTimestamp-mAsyncOpenTime).ToMilliseconds();uint64_tonStopTime=(TimeStamp::Now()-mAsyncOpenTime).ToMilliseconds();Unused<<mCacheEntry->SetNetworkTimes(onStartTime,onStopTime);}// at this point, we're done with the transactionmTransactionTimings=mTransaction->Timings();mTransaction=nullptr;mTransactionPump=nullptr;// We no longer need the dns prefetch objectif(mDNSPrefetch&&mDNSPrefetch->TimingsValid()&&!mTransactionTimings.requestStart.IsNull()&&!mTransactionTimings.connectStart.IsNull()&&mDNSPrefetch->EndTimestamp()<=mTransactionTimings.connectStart){// We only need the domainLookup timestamps when not using a// persistent connection, meaning if the endTimestamp < connectStartmTransactionTimings.domainLookupStart=mDNSPrefetch->StartTimestamp();mTransactionTimings.domainLookupEnd=mDNSPrefetch->EndTimestamp();}mDNSPrefetch=nullptr;// handle auth retry...if(authRetry){mAuthRetryPending=false;status=DoAuthRetry(conn);if(NS_SUCCEEDED(status))returnNS_OK;}// If DoAuthRetry failed, or if we have been cancelled since showing// the auth. dialog, then we need to send OnStartRequest nowif(authRetry||(mAuthRetryPending&&NS_FAILED(status))){MOZ_ASSERT(NS_FAILED(status),"should have a failure code here");// NOTE: since we have a failure status, we can ignore the return// value from onStartRequest.if(mListener){MOZ_ASSERT(!mOnStartRequestCalled,"We should not call OnStartRequest twice.");mListener->OnStartRequest(this,mListenerContext);mOnStartRequestCalled=true;}else{NS_WARNING("OnStartRequest skipped because of null listener");}}// if this transaction has been replaced, then bail.if(mTransactionReplaced){LOG(("Transaction replaced\n"));// This was just the network check for a 304 response.mFirstResponseSource=RESPONSE_PENDING;returnNS_OK;}if(mUpgradeProtocolCallback&&stickyConn&&mResponseHead&&mResponseHead->Status()==101){nsresultrv=gHttpHandler->ConnMgr()->CompleteUpgrade(stickyConn,mUpgradeProtocolCallback);if(NS_FAILED(rv)){LOG((" CompleteUpgrade failed with %08x",static_cast<uint32_t>(rv)));}}}// HTTP_CHANNEL_DISPOSITION TELEMETRYenumChannelDisposition{kHttpCanceled=0,kHttpDisk=1,kHttpNetOK=2,kHttpNetEarlyFail=3,kHttpNetLateFail=4,kHttpsCanceled=8,kHttpsDisk=9,kHttpsNetOK=10,kHttpsNetEarlyFail=11,kHttpsNetLateFail=12}chanDisposition=kHttpCanceled;// HTTP 0.9 is more likely to be an error than really 0.9, so count it that wayif(mCanceled){chanDisposition=kHttpCanceled;}elseif(!mUsedNetwork){chanDisposition=kHttpDisk;}elseif(NS_SUCCEEDED(status)&&mResponseHead&&mResponseHead->Version()!=NS_HTTP_VERSION_0_9){chanDisposition=kHttpNetOK;}elseif(!mTransferSize){chanDisposition=kHttpNetEarlyFail;}else{chanDisposition=kHttpNetLateFail;}if(IsHTTPS()){// shift http to https disposition enumschanDisposition=static_cast<ChannelDisposition>(chanDisposition+kHttpsCanceled);}LOG((" nsHttpChannel::OnStopRequest ChannelDisposition %d\n",chanDisposition));Telemetry::Accumulate(Telemetry::HTTP_CHANNEL_DISPOSITION,chanDisposition);// if needed, check cache entry has all data we expectif(mCacheEntry&&mCachePump&&mConcurrentCacheAccess&&contentComplete){int64_tsize,contentLength;nsresultrv=CheckPartial(mCacheEntry,&size,&contentLength);if(NS_SUCCEEDED(rv)){if(size==int64_t(-1)){// mayhemer TODO - we have to restart read from cache here at the size offsetMOZ_ASSERT(false);LOG((" cache entry write is still in progress, but we just ""finished reading the cache entry"));}elseif(contentLength!=int64_t(-1)&&contentLength!=size){LOG((" concurrent cache entry write has been interrupted"));mCachedResponseHead=Move(mResponseHead);// Ignore zero partial length because we also want to resume when// no data at all has been read from the cache.rv=MaybeSetupByteRangeRequest(size,contentLength,true);if(NS_SUCCEEDED(rv)&&mIsPartialRequest){// Prevent read from cache againmCachedContentIsValid=0;mCachedContentIsPartial=1;// Perform the range requestrv=ContinueConnect();if(NS_SUCCEEDED(rv)){LOG((" performing range request"));mCachePump=nullptr;returnNS_OK;}else{LOG((" but range request perform failed 0x%08"PRIx32,static_cast<uint32_t>(rv)));status=NS_ERROR_NET_INTERRUPT;}}else{LOG((" but range request setup failed rv=0x%08"PRIx32", failing load",static_cast<uint32_t>(rv)));}}}}mIsPending=false;mStatus=status;// perform any final cache operations before we close the cache entry.if(mCacheEntry&&mRequestTimeInitialized){boolwriteAccess;// New implementation just returns value of the !mCacheEntryIsReadOnly flag passed in.// Old implementation checks on nsICache::ACCESS_WRITE flag.mCacheEntry->HasWriteAccess(!mCacheEntryIsReadOnly,&writeAccess);if(writeAccess){nsresultrv=FinalizeCacheEntry();if(NS_FAILED(rv)){LOG(("FinalizeCacheEntry failed (%08x)",static_cast<uint32_t>(rv)));}}}ReportRcwnStats(request,isFromNet);// Register entry to the Performance resource timingmozilla::dom::Performance*documentPerformance=GetPerformance();if(documentPerformance){documentPerformance->AddEntry(this,this);}if(mListener){LOG((" calling OnStopRequest\n"));MOZ_ASSERT(mOnStartRequestCalled,"OnStartRequest should be called before OnStopRequest");MOZ_ASSERT(!mOnStopRequestCalled,"We should not call OnStopRequest twice");mListener->OnStopRequest(this,mListenerContext,status);mOnStopRequestCalled=true;}// If a preferred alt-data type was set, this signals the consumer is// interested in reading and/or writing the alt-data representation.// We need to hold a reference to the cache entry in case the listener calls// openAlternativeOutputStream() after CloseCacheEntry() clears mCacheEntry.if(!mPreferredCachedAltDataType.IsEmpty()){mAltDataCacheEntry=mCacheEntry;}CloseCacheEntry(!contentComplete);if(mOfflineCacheEntry)CloseOfflineCacheEntry();if(mLoadGroup)mLoadGroup->RemoveRequest(this,nullptr,status);// We don't need this info anymoreCleanRedirectCacheChainIfNecessary();ReleaseListeners();returnNS_OK;}//-----------------------------------------------------------------------------// nsHttpChannel::nsIStreamListener//-----------------------------------------------------------------------------classOnTransportStatusAsyncEvent:publicRunnable{public:OnTransportStatusAsyncEvent(nsITransportEventSink*aEventSink,nsresultaTransportStatus,int64_taProgress,int64_taProgressMax):Runnable("net::OnTransportStatusAsyncEvent"),mEventSink(aEventSink),mTransportStatus(aTransportStatus),mProgress(aProgress),mProgressMax(aProgressMax){MOZ_ASSERT(!NS_IsMainThread(),"Shouldn't be created on main thread");}NS_IMETHODRun()override{MOZ_ASSERT(NS_IsMainThread(),"Should run on main thread");if(mEventSink){mEventSink->OnTransportStatus(nullptr,mTransportStatus,mProgress,mProgressMax);}returnNS_OK;}private:nsCOMPtr<nsITransportEventSink>mEventSink;nsresultmTransportStatus;int64_tmProgress;int64_tmProgressMax;};NS_IMETHODIMPnsHttpChannel::OnDataAvailable(nsIRequest*request,nsISupports*ctxt,nsIInputStream*input,uint64_toffset,uint32_tcount){nsresultrv;AUTO_PROFILER_LABEL("nsHttpChannel::OnDataAvailable",NETWORK);LOG(("nsHttpChannel::OnDataAvailable [this=%p request=%p offset=%"PRIu64" count=%"PRIu32"]\n",this,request,offset,count));LOG((" requestFromCache: %d mFirstResponseSource: %d\n",request==mCachePump,mFirstResponseSource));// don't send out OnDataAvailable notifications if we've been canceled.if(mCanceled)returnmStatus;if(mAuthRetryPending||WRONG_RACING_RESPONSE_SOURCE(request)||(request==mTransactionPump&&mTransactionReplaced)){uint32_tn;returninput->ReadSegments(NS_DiscardSegment,nullptr,count,&n);}MOZ_ASSERT(mResponseHead,"No response head in ODA!!");MOZ_ASSERT(!(mCachedContentIsPartial&&(request==mTransactionPump)),"transaction pump not suspended");mIsReadingFromCache=(request==mCachePump);if(mListener){//// synthesize transport progress event. we do this here since we want// to delay OnProgress events until we start streaming data. this is// crucially important since it impacts the lock icon (see bug 240053).//nsresulttransportStatus;if(request==mCachePump)transportStatus=NS_NET_STATUS_READING;elsetransportStatus=NS_NET_STATUS_RECEIVING_FROM;// mResponseHead may reference new or cached headers, but either way it// holds our best estimate of the total content length. Even in the case// of a byte range request, the content length stored in the cached// response headers is what we want to use here.int64_tprogressMax=-1;rv=GetContentLength(&progressMax);if(NS_FAILED(rv)){NS_WARNING("GetContentLength failed");}int64_tprogress=mLogicalOffset+count;if((progress>progressMax)&&(progressMax!=-1)){NS_WARNING("unexpected progress values - ""is server exceeding content length?");}// make sure params are in range for jsif(!InScriptableRange(progressMax)){progressMax=-1;}if(!InScriptableRange(progress)){progress=-1;}if(NS_IsMainThread()){OnTransportStatus(nullptr,transportStatus,progress,progressMax);}else{rv=NS_DispatchToMainThread(newOnTransportStatusAsyncEvent(this,transportStatus,progress,progressMax));NS_ENSURE_SUCCESS(rv,rv);}//// we have to manually keep the logical offset of the stream up-to-date.// we cannot depend solely on the offset provided, since we may have// already streamed some data from another source (see, for example,// OnDoneReadingPartialCacheEntry).//int64_toffsetBefore=0;nsCOMPtr<nsISeekableStream>seekable=do_QueryInterface(input);if(seekable&&NS_FAILED(seekable->Tell(&offsetBefore))){seekable=nullptr;}nsresultrv=mListener->OnDataAvailable(this,mListenerContext,input,mLogicalOffset,count);if(NS_SUCCEEDED(rv)){// by contract mListener must read all of "count" bytes, but// nsInputStreamPump is tolerant to seekable streams that violate that// and it will redeliver incompletely read data. So we need to do// the same thing when updating the progress counter to stay in sync.int64_toffsetAfter,delta;if(seekable&&NS_SUCCEEDED(seekable->Tell(&offsetAfter))){delta=offsetAfter-offsetBefore;if(delta!=count){count=delta;NS_WARNING("Listener OnDataAvailable contract violation");nsCOMPtr<nsIConsoleService>consoleService=do_GetService(NS_CONSOLESERVICE_CONTRACTID);nsAutoStringmessage(NS_LITERAL_STRING("http channel Listener OnDataAvailable contract violation"));if(consoleService){consoleService->LogStringMessage(message.get());}}}mLogicalOffset+=count;}returnrv;}returnNS_ERROR_ABORT;}//-----------------------------------------------------------------------------// nsHttpChannel::nsIThreadRetargetableRequest//-----------------------------------------------------------------------------NS_IMETHODIMPnsHttpChannel::RetargetDeliveryTo(nsIEventTarget*aNewTarget){MOZ_ASSERT(NS_IsMainThread(),"Should be called on main thread only");NS_ENSURE_ARG(aNewTarget);if(aNewTarget->IsOnCurrentThread()){NS_WARNING("Retargeting delivery to same thread");returnNS_OK;}if(!mTransactionPump&&!mCachePump){LOG(("nsHttpChannel::RetargetDeliveryTo %p %p no pump available\n",this,aNewTarget));returnNS_ERROR_NOT_AVAILABLE;}nsresultrv=NS_OK;// If both cache pump and transaction pump exist, we're probably dealing// with partially cached content. So, we must be able to retarget both.nsCOMPtr<nsIThreadRetargetableRequest>retargetableCachePump;nsCOMPtr<nsIThreadRetargetableRequest>retargetableTransactionPump;if(mCachePump){retargetableCachePump=do_QueryObject(mCachePump);// nsInputStreamPump should implement this interface.MOZ_ASSERT(retargetableCachePump);rv=retargetableCachePump->RetargetDeliveryTo(aNewTarget);}if(NS_SUCCEEDED(rv)&&mTransactionPump){retargetableTransactionPump=do_QueryObject(mTransactionPump);// nsInputStreamPump should implement this interface.MOZ_ASSERT(retargetableTransactionPump);rv=retargetableTransactionPump->RetargetDeliveryTo(aNewTarget);// If retarget fails for transaction pump, we must restore mCachePump.if(NS_FAILED(rv)&&retargetableCachePump){nsCOMPtr<nsIEventTarget>main=GetMainThreadEventTarget();NS_ENSURE_TRUE(main,NS_ERROR_UNEXPECTED);rv=retargetableCachePump->RetargetDeliveryTo(main);}}returnrv;}//-----------------------------------------------------------------------------// nsHttpChannel::nsThreadRetargetableStreamListener//-----------------------------------------------------------------------------NS_IMETHODIMPnsHttpChannel::CheckListenerChain(){NS_ASSERTION(NS_IsMainThread(),"Should be on main thread!");nsresultrv=NS_OK;nsCOMPtr<nsIThreadRetargetableStreamListener>retargetableListener=do_QueryInterface(mListener,&rv);if(retargetableListener){rv=retargetableListener->CheckListenerChain();}returnrv;}//-----------------------------------------------------------------------------// nsHttpChannel::nsITransportEventSink//-----------------------------------------------------------------------------NS_IMETHODIMPnsHttpChannel::OnTransportStatus(nsITransport*trans,nsresultstatus,int64_tprogress,int64_tprogressMax){MOZ_ASSERT(NS_IsMainThread(),"Should be on main thread only");// cache the progress sink so we don't have to query for it each time.if(!mProgressSink)GetCallback(mProgressSink);if(status==NS_NET_STATUS_CONNECTED_TO||status==NS_NET_STATUS_WAITING_FOR){if(mTransaction){mTransaction->GetNetworkAddresses(mSelfAddr,mPeerAddr);}else{nsCOMPtr<nsISocketTransport>socketTransport=do_QueryInterface(trans);if(socketTransport){socketTransport->GetSelfAddr(&mSelfAddr);socketTransport->GetPeerAddr(&mPeerAddr);}}}// block socket status event after Cancel or OnStopRequest has been called.if(mProgressSink&&NS_SUCCEEDED(mStatus)&&mIsPending){LOG(("sending progress%s notification [this=%p status=%"PRIx32" progress=%"PRId64"/%"PRId64"]\n",(mLoadFlags&LOAD_BACKGROUND)?"":" and status",this,static_cast<uint32_t>(status),progress,progressMax));if(!(mLoadFlags&LOAD_BACKGROUND)){nsAutoCStringhost;mURI->GetHost(host);mProgressSink->OnStatus(this,nullptr,status,NS_ConvertUTF8toUTF16(host).get());}if(progress>0){if((progress>progressMax)&&(progressMax!=-1)){NS_WARNING("unexpected progress values");}// Try to get mProgressSink if it was nulled out during OnStatus.if(!mProgressSink){GetCallback(mProgressSink);}if(mProgressSink){mProgressSink->OnProgress(this,nullptr,progress,progressMax);}}}returnNS_OK;}//-----------------------------------------------------------------------------// nsHttpChannel::nsICacheInfoChannel//-----------------------------------------------------------------------------NS_IMETHODIMPnsHttpChannel::IsFromCache(bool*value){if(!mIsPending)returnNS_ERROR_NOT_AVAILABLE;if(!mRaceCacheWithNetwork){// return false if reading a partial cache entry; the data isn't// entirely from the cache!*value=(mCachePump||(mLoadFlags&LOAD_ONLY_IF_MODIFIED))&&mCachedContentIsValid&&!mCachedContentIsPartial;returnNS_OK;}// If we are racing network and cache (or skipping the cache)// we just return the first response source.*value=mFirstResponseSource==RESPONSE_FROM_CACHE;returnNS_OK;}NS_IMETHODIMPnsHttpChannel::GetCacheTokenFetchCount(int32_t*_retval){NS_ENSURE_ARG_POINTER(_retval);nsCOMPtr<nsICacheEntry>cacheEntry=mCacheEntry?mCacheEntry:mAltDataCacheEntry;if(!cacheEntry){returnNS_ERROR_NOT_AVAILABLE;}returncacheEntry->GetFetchCount(_retval);}NS_IMETHODIMPnsHttpChannel::GetCacheTokenLastFetched(uint32_t*_retval){NS_ENSURE_ARG_POINTER(_retval);nsCOMPtr<nsICacheEntry>cacheEntry=mCacheEntry?mCacheEntry:mAltDataCacheEntry;if(!cacheEntry){returnNS_ERROR_NOT_AVAILABLE;}returncacheEntry->GetLastFetched(_retval);}NS_IMETHODIMPnsHttpChannel::GetCacheTokenExpirationTime(uint32_t*_retval){NS_ENSURE_ARG_POINTER(_retval);if(!mCacheEntry)returnNS_ERROR_NOT_AVAILABLE;returnmCacheEntry->GetExpirationTime(_retval);}NS_IMETHODIMPnsHttpChannel::GetCacheTokenCachedCharset(nsACString&_retval){nsresultrv;if(!mCacheEntry)returnNS_ERROR_NOT_AVAILABLE;nsXPIDLCStringcachedCharset;rv=mCacheEntry->GetMetaDataElement("charset",getter_Copies(cachedCharset));if(NS_SUCCEEDED(rv))_retval=cachedCharset;returnrv;}NS_IMETHODIMPnsHttpChannel::SetCacheTokenCachedCharset(constnsACString&aCharset){if(!mCacheEntry)returnNS_ERROR_NOT_AVAILABLE;returnmCacheEntry->SetMetaDataElement("charset",PromiseFlatCString(aCharset).get());}NS_IMETHODIMPnsHttpChannel::SetAllowStaleCacheContent(boolaAllowStaleCacheContent){LOG(("nsHttpChannel::SetAllowStaleCacheContent [this=%p, allow=%d]",this,aAllowStaleCacheContent));mAllowStaleCacheContent=aAllowStaleCacheContent;returnNS_OK;}NS_IMETHODIMPnsHttpChannel::GetAllowStaleCacheContent(bool*aAllowStaleCacheContent){NS_ENSURE_ARG(aAllowStaleCacheContent);*aAllowStaleCacheContent=mAllowStaleCacheContent;returnNS_OK;}NS_IMETHODIMPnsHttpChannel::PreferAlternativeDataType(constnsACString&aType){ENSURE_CALLED_BEFORE_ASYNC_OPEN();mPreferredCachedAltDataType=aType;returnNS_OK;}NS_IMETHODIMPnsHttpChannel::GetAlternativeDataType(nsACString&aType){// must be called during or after OnStartRequestif(!mAfterOnStartRequestBegun){returnNS_ERROR_NOT_AVAILABLE;}aType=mAvailableCachedAltDataType;returnNS_OK;}NS_IMETHODIMPnsHttpChannel::OpenAlternativeOutputStream(constnsACString&type,nsIOutputStream**_retval){// OnStopRequest will clear mCacheEntry, but we may use mAltDataCacheEntry// if the consumer called PreferAlternativeDataType()nsCOMPtr<nsICacheEntry>cacheEntry=mCacheEntry?mCacheEntry:mAltDataCacheEntry;if(!cacheEntry){returnNS_ERROR_NOT_AVAILABLE;}returncacheEntry->OpenAlternativeOutputStream(type,_retval);}//-----------------------------------------------------------------------------// nsHttpChannel::nsICachingChannel//-----------------------------------------------------------------------------NS_IMETHODIMPnsHttpChannel::GetCacheToken(nsISupports**token){NS_ENSURE_ARG_POINTER(token);if(!mCacheEntry)returnNS_ERROR_NOT_AVAILABLE;returnCallQueryInterface(mCacheEntry,token);}NS_IMETHODIMPnsHttpChannel::SetCacheToken(nsISupports*token){returnNS_ERROR_NOT_IMPLEMENTED;}NS_IMETHODIMPnsHttpChannel::GetOfflineCacheToken(nsISupports**token){NS_ENSURE_ARG_POINTER(token);if(!mOfflineCacheEntry)returnNS_ERROR_NOT_AVAILABLE;returnCallQueryInterface(mOfflineCacheEntry,token);}NS_IMETHODIMPnsHttpChannel::SetOfflineCacheToken(nsISupports*token){returnNS_ERROR_NOT_IMPLEMENTED;}NS_IMETHODIMPnsHttpChannel::GetCacheKey(nsISupports**key){nsresultrv;NS_ENSURE_ARG_POINTER(key);LOG(("nsHttpChannel::GetCacheKey [this=%p]\n",this));*key=nullptr;nsCOMPtr<nsISupportsPRUint32>container=do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID,&rv);if(!container)returnNS_ERROR_OUT_OF_MEMORY;rv=container->SetData(mPostID);if(NS_FAILED(rv))returnrv;returnCallQueryInterface(container.get(),key);}NS_IMETHODIMPnsHttpChannel::SetCacheKey(nsISupports*key){nsresultrv;LOG(("nsHttpChannel::SetCacheKey [this=%p key=%p]\n",this,key));ENSURE_CALLED_BEFORE_CONNECT();if(!key)mPostID=0;else{// extract the post idnsCOMPtr<nsISupportsPRUint32>container=do_QueryInterface(key,&rv);if(NS_FAILED(rv))returnrv;rv=container->GetData(&mPostID);if(NS_FAILED(rv))returnrv;}returnNS_OK;}NS_IMETHODIMPnsHttpChannel::GetCacheOnlyMetadata(bool*aOnlyMetadata){NS_ENSURE_ARG(aOnlyMetadata);*aOnlyMetadata=mCacheOnlyMetadata;returnNS_OK;}NS_IMETHODIMPnsHttpChannel::SetCacheOnlyMetadata(boolaOnlyMetadata){LOG(("nsHttpChannel::SetCacheOnlyMetadata [this=%p only-metadata=%d]\n",this,aOnlyMetadata));ENSURE_CALLED_BEFORE_ASYNC_OPEN();mCacheOnlyMetadata=aOnlyMetadata;if(aOnlyMetadata){mLoadFlags|=LOAD_ONLY_IF_MODIFIED;}returnNS_OK;}NS_IMETHODIMPnsHttpChannel::GetPin(bool*aPin){NS_ENSURE_ARG(aPin);*aPin=mPinCacheContent;returnNS_OK;}NS_IMETHODIMPnsHttpChannel::SetPin(boolaPin){LOG(("nsHttpChannel::SetPin [this=%p pin=%d]\n",this,aPin));ENSURE_CALLED_BEFORE_CONNECT();mPinCacheContent=aPin;returnNS_OK;}NS_IMETHODIMPnsHttpChannel::ForceCacheEntryValidFor(uint32_taSecondsToTheFuture){if(!mCacheEntry){LOG(("nsHttpChannel::ForceCacheEntryValidFor found no cache entry ""for this channel [this=%p].",this));}else{mCacheEntry->ForceValidFor(aSecondsToTheFuture);nsAutoCStringkey;mCacheEntry->GetKey(key);LOG(("nsHttpChannel::ForceCacheEntryValidFor successfully forced valid ""entry with key %s for %d seconds. [this=%p]",key.get(),aSecondsToTheFuture,this));}returnNS_OK;}//-----------------------------------------------------------------------------// nsHttpChannel::nsIResumableChannel//-----------------------------------------------------------------------------NS_IMETHODIMPnsHttpChannel::ResumeAt(uint64_taStartPos,constnsACString&aEntityID){LOG(("nsHttpChannel::ResumeAt [this=%p startPos=%"PRIu64" id='%s']\n",this,aStartPos,PromiseFlatCString(aEntityID).get()));mEntityID=aEntityID;mStartPos=aStartPos;mResuming=true;returnNS_OK;}nsresultnsHttpChannel::DoAuthRetry(nsAHttpConnection*conn){LOG(("nsHttpChannel::DoAuthRetry [this=%p]\n",this));MOZ_ASSERT(!mTransaction,"should not have a transaction");nsresultrv;// toggle mIsPending to allow nsIObserver implementations to modify// the request headers (bug 95044).mIsPending=false;// fetch cookies, and add them to the request header.// the server response could have included cookies that must be sent with// this authentication attempt (bug 84794).// TODO: save cookies from auth response and send them here (bug 572151).AddCookiesToRequest();// notify "http-on-modify-request" observersCallOnModifyRequestObservers();mIsPending=true;// get rid of the old response headersmResponseHead=nullptr;// rewind the upload streamif(mUploadStream){nsCOMPtr<nsISeekableStream>seekable=do_QueryInterface(mUploadStream);if(seekable)seekable->Seek(nsISeekableStream::NS_SEEK_SET,0);}// always set sticky connection flagmCaps|=NS_HTTP_STICKY_CONNECTION;// and when needed, allow restart regardless the sticky flagif(mAuthConnectionRestartable){LOG((" connection made restartable"));mCaps|=NS_HTTP_CONNECTION_RESTARTABLE;mAuthConnectionRestartable=false;}else{LOG((" connection made non-restartable"));mCaps&=~NS_HTTP_CONNECTION_RESTARTABLE;}// and create a new one...rv=SetupTransaction();if(NS_FAILED(rv))returnrv;// transfer ownership of connection to transactionif(conn)mTransaction->SetConnection(conn);rv=gHttpHandler->InitiateTransaction(mTransaction,mPriority);if(NS_FAILED(rv))returnrv;rv=mTransactionPump->AsyncRead(this,nullptr);if(NS_FAILED(rv))returnrv;uint32_tsuspendCount=mSuspendCount;while(suspendCount--)mTransactionPump->Suspend();returnNS_OK;}//-----------------------------------------------------------------------------// nsHttpChannel::nsIApplicationCacheChannel//-----------------------------------------------------------------------------NS_IMETHODIMPnsHttpChannel::GetApplicationCache(nsIApplicationCache**out){NS_IF_ADDREF(*out=mApplicationCache);returnNS_OK;}NS_IMETHODIMPnsHttpChannel::SetApplicationCache(nsIApplicationCache*appCache){ENSURE_CALLED_BEFORE_CONNECT();mApplicationCache=appCache;returnNS_OK;}NS_IMETHODIMPnsHttpChannel::GetApplicationCacheForWrite(nsIApplicationCache**out){NS_IF_ADDREF(*out=mApplicationCacheForWrite);returnNS_OK;}NS_IMETHODIMPnsHttpChannel::SetApplicationCacheForWrite(nsIApplicationCache*appCache){ENSURE_CALLED_BEFORE_CONNECT();mApplicationCacheForWrite=appCache;returnNS_OK;}NS_IMETHODIMPnsHttpChannel::GetLoadedFromApplicationCache(bool*aLoadedFromApplicationCache){*aLoadedFromApplicationCache=mLoadedFromApplicationCache;returnNS_OK;}NS_IMETHODIMPnsHttpChannel::GetInheritApplicationCache(bool*aInherit){*aInherit=mInheritApplicationCache;returnNS_OK;}NS_IMETHODIMPnsHttpChannel::SetInheritApplicationCache(boolaInherit){ENSURE_CALLED_BEFORE_CONNECT();mInheritApplicationCache=aInherit;returnNS_OK;}NS_IMETHODIMPnsHttpChannel::GetChooseApplicationCache(bool*aChoose){*aChoose=mChooseApplicationCache;returnNS_OK;}NS_IMETHODIMPnsHttpChannel::SetChooseApplicationCache(boolaChoose){ENSURE_CALLED_BEFORE_CONNECT();mChooseApplicationCache=aChoose;returnNS_OK;}nsHttpChannel::OfflineCacheEntryAsForeignMarker*nsHttpChannel::GetOfflineCacheEntryAsForeignMarker(){if(!mApplicationCache)returnnullptr;returnnewOfflineCacheEntryAsForeignMarker(mApplicationCache,mURI);}nsresultnsHttpChannel::OfflineCacheEntryAsForeignMarker::MarkAsForeign(){nsresultrv;nsCOMPtr<nsIURI>noRefURI;rv=mCacheURI->CloneIgnoringRef(getter_AddRefs(noRefURI));NS_ENSURE_SUCCESS(rv,rv);nsAutoCStringspec;rv=noRefURI->GetAsciiSpec(spec);NS_ENSURE_SUCCESS(rv,rv);returnmApplicationCache->MarkEntry(spec,nsIApplicationCache::ITEM_FOREIGN);}NS_IMETHODIMPnsHttpChannel::MarkOfflineCacheEntryAsForeign(){nsresultrv;nsAutoPtr<OfflineCacheEntryAsForeignMarker>marker(GetOfflineCacheEntryAsForeignMarker());if(!marker)returnNS_ERROR_NOT_AVAILABLE;rv=marker->MarkAsForeign();NS_ENSURE_SUCCESS(rv,rv);returnNS_OK;}//-----------------------------------------------------------------------------// nsHttpChannel::nsIAsyncVerifyRedirectCallback//-----------------------------------------------------------------------------nsresultnsHttpChannel::WaitForRedirectCallback(){nsresultrv;LOG(("nsHttpChannel::WaitForRedirectCallback [this=%p]\n",this));if(mTransactionPump){rv=mTransactionPump->Suspend();NS_ENSURE_SUCCESS(rv,rv);}if(mCachePump){rv=mCachePump->Suspend();if(NS_FAILED(rv)&&mTransactionPump){#ifdef DEBUGnsresultresume=#endifmTransactionPump->Resume();MOZ_ASSERT(NS_SUCCEEDED(resume),"Failed to resume transaction pump");}NS_ENSURE_SUCCESS(rv,rv);}mWaitingForRedirectCallback=true;returnNS_OK;}NS_IMETHODIMPnsHttpChannel::OnRedirectVerifyCallback(nsresultresult){LOG(("nsHttpChannel::OnRedirectVerifyCallback [this=%p] ""result=%"PRIx32" stack=%"PRIuSIZE" mWaitingForRedirectCallback=%u\n",this,static_cast<uint32_t>(result),mRedirectFuncStack.Length(),mWaitingForRedirectCallback));MOZ_ASSERT(mWaitingForRedirectCallback,"Someone forgot to call WaitForRedirectCallback() ?!");mWaitingForRedirectCallback=false;if(mCanceled&&NS_SUCCEEDED(result))result=NS_BINDING_ABORTED;for(uint32_ti=mRedirectFuncStack.Length();i>0;){--i;// Pop the last function pushed to the stacknsContinueRedirectionFuncfunc=mRedirectFuncStack[i];mRedirectFuncStack.RemoveElementAt(mRedirectFuncStack.Length()-1);// Call it with the result we got from the callback or the deeper// function call.result=(this->*func)(result);// If a new function has been pushed to the stack and placed us in the// waiting state, we need to break the chain and wait for the callback// again.if(mWaitingForRedirectCallback)break;}if(NS_FAILED(result)&&!mCanceled){// First, cancel this channel if we are in failure state to set mStatus// and let it be propagated to pumps.Cancel(result);}if(!mWaitingForRedirectCallback){// We are not waiting for the callback. At this moment we must release// reference to the redirect target channel, otherwise we may leak.mRedirectChannel=nullptr;}// We always resume the pumps here. If all functions on stack have been// called we need OnStopRequest to be triggered, and if we broke out of the// loop above (and are thus waiting for a new callback) the suspension// count must be balanced in the pumps.if(mTransactionPump)mTransactionPump->Resume();if(mCachePump)mCachePump->Resume();returnresult;}voidnsHttpChannel::PushRedirectAsyncFunc(nsContinueRedirectionFuncfunc){mRedirectFuncStack.AppendElement(func);}voidnsHttpChannel::PopRedirectAsyncFunc(nsContinueRedirectionFuncfunc){MOZ_ASSERT(func==mRedirectFuncStack[mRedirectFuncStack.Length()-1],"Trying to pop wrong method from redirect async stack!");mRedirectFuncStack.TruncateLength(mRedirectFuncStack.Length()-1);}//-----------------------------------------------------------------------------// nsIDNSListener functions//-----------------------------------------------------------------------------NS_IMETHODIMPnsHttpChannel::OnLookupComplete(nsICancelable*request,nsIDNSRecord*rec,nsresultstatus){MOZ_ASSERT(NS_IsMainThread(),"Expecting DNS callback on main thread.");LOG(("nsHttpChannel::OnLookupComplete [this=%p] prefetch complete%s: ""%s status[0x%"PRIx32"]\n",this,mCaps&NS_HTTP_REFRESH_DNS?", refresh requested":"",NS_SUCCEEDED(status)?"success":"failure",static_cast<uint32_t>(status)));// We no longer need the dns prefetch object. Note: mDNSPrefetch could be// validly null if OnStopRequest has already been called.// We only need the domainLookup timestamps when not loading from cacheif(mDNSPrefetch&&mDNSPrefetch->TimingsValid()&&mTransaction){TimeStampconnectStart=mTransaction->GetConnectStart();TimeStamprequestStart=mTransaction->GetRequestStart();// We only set the domainLookup timestamps if we're not using a// persistent connection.if(requestStart.IsNull()&&connectStart.IsNull()){mTransaction->SetDomainLookupStart(mDNSPrefetch->StartTimestamp());mTransaction->SetDomainLookupEnd(mDNSPrefetch->EndTimestamp());}}mDNSPrefetch=nullptr;// Unset DNS cache refresh if it was requested,if(mCaps&NS_HTTP_REFRESH_DNS){mCaps&=~NS_HTTP_REFRESH_DNS;if(mTransaction){mTransaction->SetDNSWasRefreshed();}}returnNS_OK;}//-----------------------------------------------------------------------------// nsHttpChannel internal functions//-----------------------------------------------------------------------------// Creates an URI to the given location using current URI for base and charsetnsresultnsHttpChannel::CreateNewURI(constchar*loc,nsIURI**newURI){nsCOMPtr<nsIIOService>ioService;nsresultrv=gHttpHandler->GetIOService(getter_AddRefs(ioService));if(NS_FAILED(rv))returnrv;// the new uri should inherit the origin charset of the current urinsAutoCStringoriginCharset;rv=mURI->GetOriginCharset(originCharset);if(NS_FAILED(rv))originCharset.Truncate();returnioService->NewURI(nsDependentCString(loc),originCharset.get(),mURI,newURI);}voidnsHttpChannel::MaybeInvalidateCacheEntryForSubsequentGet(){// See RFC 2616 section 5.1.1. These are considered valid// methods which DO NOT invalidate cache-entries for the// referred resource. POST, PUT and DELETE as well as any// other method not listed here will potentially invalidate// any cached copy of the resourceif(mRequestHead.IsGet()||mRequestHead.IsOptions()||mRequestHead.IsHead()||mRequestHead.IsTrace()||mRequestHead.IsConnect()){return;}// Invalidate the request-uri.if(LOG_ENABLED()){nsAutoCStringkey;mURI->GetAsciiSpec(key);LOG(("MaybeInvalidateCacheEntryForSubsequentGet [this=%p uri=%s]\n",this,key.get()));}DoInvalidateCacheEntry(mURI);// Invalidate Location-header if setnsAutoCStringlocation;Unused<<mResponseHead->GetHeader(nsHttp::Location,location);if(!location.IsEmpty()){LOG((" Location-header=%s\n",location.get()));InvalidateCacheEntryForLocation(location.get());}// Invalidate Content-Location-header if setUnused<<mResponseHead->GetHeader(nsHttp::Content_Location,location);if(!location.IsEmpty()){LOG((" Content-Location-header=%s\n",location.get()));InvalidateCacheEntryForLocation(location.get());}}voidnsHttpChannel::InvalidateCacheEntryForLocation(constchar*location){nsAutoCStringtmpCacheKey,tmpSpec;nsCOMPtr<nsIURI>resultingURI;nsresultrv=CreateNewURI(location,getter_AddRefs(resultingURI));if(NS_SUCCEEDED(rv)&&HostPartIsTheSame(resultingURI)){DoInvalidateCacheEntry(resultingURI);}else{LOG((" hosts not matching\n"));}}voidnsHttpChannel::DoInvalidateCacheEntry(nsIURI*aURI){// NOTE:// Following comments 24,32 and 33 in bug #327765, we only care about// the cache in the protocol-handler, not the application cache.// The logic below deviates from the original logic in OpenCacheEntry on// one point by using only READ_ONLY access-policy. I think this is safe.nsresultrv;nsAutoCStringkey;if(LOG_ENABLED()){aURI->GetAsciiSpec(key);}LOG(("DoInvalidateCacheEntry [channel=%p key=%s]",this,key.get()));nsCOMPtr<nsICacheStorageService>cacheStorageService(services::GetCacheStorageService());rv=cacheStorageService?NS_OK:NS_ERROR_FAILURE;nsCOMPtr<nsICacheStorage>cacheStorage;if(NS_SUCCEEDED(rv)){RefPtr<LoadContextInfo>info=GetLoadContextInfo(this);rv=cacheStorageService->DiskCacheStorage(info,false,getter_AddRefs(cacheStorage));}if(NS_SUCCEEDED(rv)){rv=cacheStorage->AsyncDoomURI(aURI,EmptyCString(),nullptr);}LOG(("DoInvalidateCacheEntry [channel=%p key=%s rv=%d]",this,key.get(),int(rv)));}voidnsHttpChannel::AsyncOnExamineCachedResponse(){gHttpHandler->OnExamineCachedResponse(this);}voidnsHttpChannel::UpdateAggregateCallbacks(){if(!mTransaction){return;}nsCOMPtr<nsIInterfaceRequestor>callbacks;NS_NewNotificationCallbacksAggregation(mCallbacks,mLoadGroup,GetCurrentThreadEventTarget(),getter_AddRefs(callbacks));mTransaction->SetSecurityCallbacks(callbacks);}NS_IMETHODIMPnsHttpChannel::SetLoadGroup(nsILoadGroup*aLoadGroup){MOZ_ASSERT(NS_IsMainThread(),"Wrong thread.");nsresultrv=HttpBaseChannel::SetLoadGroup(aLoadGroup);if(NS_SUCCEEDED(rv)){UpdateAggregateCallbacks();}returnrv;}NS_IMETHODIMPnsHttpChannel::SetNotificationCallbacks(nsIInterfaceRequestor*aCallbacks){MOZ_ASSERT(NS_IsMainThread(),"Wrong thread.");nsresultrv=HttpBaseChannel::SetNotificationCallbacks(aCallbacks);if(NS_SUCCEEDED(rv)){UpdateAggregateCallbacks();}returnrv;}voidnsHttpChannel::MarkIntercepted(){mInterceptCache=INTERCEPTED;}NS_IMETHODIMPnsHttpChannel::GetResponseSynthesized(bool*aSynthesized){NS_ENSURE_ARG_POINTER(aSynthesized);*aSynthesized=(mInterceptCache==INTERCEPTED);returnNS_OK;}boolnsHttpChannel::AwaitingCacheCallbacks(){returnmCacheEntriesToWaitFor!=0;}voidnsHttpChannel::SetPushedStream(Http2PushedStream*stream){MOZ_ASSERT(stream);MOZ_ASSERT(!mPushedStream);mPushedStream=stream;}nsresultnsHttpChannel::OnPush(constnsACString&url,Http2PushedStream*pushedStream){MOZ_ASSERT(NS_IsMainThread());LOG(("nsHttpChannel::OnPush [this=%p]\n",this));MOZ_ASSERT(mCaps&NS_HTTP_ONPUSH_LISTENER);nsCOMPtr<nsIHttpPushListener>pushListener;NS_QueryNotificationCallbacks(mCallbacks,mLoadGroup,NS_GET_IID(nsIHttpPushListener),getter_AddRefs(pushListener));MOZ_ASSERT(pushListener);if(!pushListener){LOG(("nsHttpChannel::OnPush [this=%p] notification callbacks do not ""implement nsIHttpPushListener\n",this));returnNS_ERROR_UNEXPECTED;}nsCOMPtr<nsIURI>pushResource;nsresultrv;// Create a Channel for the Push Resourcerv=NS_NewURI(getter_AddRefs(pushResource),url);if(NS_FAILED(rv)){returnNS_ERROR_FAILURE;}nsCOMPtr<nsIIOService>ioService;rv=gHttpHandler->GetIOService(getter_AddRefs(ioService));NS_ENSURE_SUCCESS(rv,rv);nsCOMPtr<nsIChannel>pushChannel;rv=NS_NewChannelInternal(getter_AddRefs(pushChannel),pushResource,mLoadInfo,nullptr,// aLoadGroupnullptr,// aCallbacksnsIRequest::LOAD_NORMAL,ioService);NS_ENSURE_SUCCESS(rv,rv);nsCOMPtr<nsIHttpChannel>pushHttpChannel=do_QueryInterface(pushChannel);MOZ_ASSERT(pushHttpChannel);if(!pushHttpChannel){returnNS_ERROR_UNEXPECTED;}RefPtr<nsHttpChannel>channel;CallQueryInterface(pushHttpChannel,channel.StartAssignment());MOZ_ASSERT(channel);if(!channel){returnNS_ERROR_UNEXPECTED;}// new channel needs mrqeuesthead and headers from pushedStreamchannel->mRequestHead.ParseHeaderSet(pushedStream->GetRequestString().BeginWriting());channel->mLoadGroup=mLoadGroup;channel->mLoadInfo=mLoadInfo;channel->mCallbacks=mCallbacks;// Link the pushed stream with the new channel and call listenerchannel->SetPushedStream(pushedStream);rv=pushListener->OnPush(this,pushHttpChannel);returnrv;}// staticboolnsHttpChannel::IsRedirectStatus(uint32_tstatus){// 305 disabled as a security measure (see bug 187996).returnstatus==300||status==301||status==302||status==303||status==307||status==308;}voidnsHttpChannel::SetCouldBeSynthesized(){MOZ_ASSERT(!BypassServiceWorker());mResponseCouldBeSynthesized=true;}voidnsHttpChannel::SetConnectionInfo(nsHttpConnectionInfo*aCI){mConnectionInfo=aCI?aCI->Clone():nullptr;}NS_IMETHODIMPnsHttpChannel::OnPreflightSucceeded(){MOZ_ASSERT(mRequireCORSPreflight,"Why did a preflight happen?");mIsCorsPreflightDone=1;mPreflightChannel=nullptr;returnContinueConnect();}NS_IMETHODIMPnsHttpChannel::OnPreflightFailed(nsresultaError){MOZ_ASSERT(mRequireCORSPreflight,"Why did a preflight happen?");mIsCorsPreflightDone=1;mPreflightChannel=nullptr;CloseCacheEntry(false);Unused<<AsyncAbort(aError);returnNS_OK;}//-----------------------------------------------------------------------------// nsIHstsPrimingCallback functions//-----------------------------------------------------------------------------/* * May be invoked synchronously if HSTS priming has already been performed * for the host. */nsresultnsHttpChannel::OnHSTSPrimingSucceeded(boolaCached){// If "security.mixed_content.use_hsts" is false, record the result of// HSTS priming and block or proceed with the load as required by// mixed-content blockingboolwouldBlock=mLoadInfo->GetMixedContentWouldBlock();// Clear out the HSTS priming flags on the LoadInfo to simplify the logic in// TryHSTSPriming()mLoadInfo->ClearHSTSPriming();if(nsMixedContentBlocker::sUseHSTS){// redirect the channel to HTTPS if the pref// "security.mixed_content.use_hsts" is trueLOG(("HSTS Priming succeeded, redirecting to HTTPS [this=%p]",this));Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_RESULT,(aCached)?HSTSPrimingResult::eHSTS_PRIMING_CACHED_DO_UPGRADE:HSTSPrimingResult::eHSTS_PRIMING_SUCCEEDED);// we have to record this upgrade here since we have already// been through NS_ShouldSecureUpgradeTelemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE,3);Telemetry::Accumulate(Telemetry::HSTS_UPGRADE_SOURCE,2);mLoadInfo->SetIsHSTSPrimingUpgrade(true);returnAsyncCall(&nsHttpChannel::HandleAsyncRedirectChannelToHttps);}// preserve the mixed-content-before-hsts order and block if requiredif(wouldBlock){LOG(("HSTS Priming succeeded, blocking for mixed-content [this=%p]",this));Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_RESULT,HSTSPrimingResult::eHSTS_PRIMING_SUCCEEDED_BLOCK);CloseCacheEntry(false);returnAsyncAbort(NS_ERROR_CONTENT_BLOCKED);}LOG(("HSTS Priming succeeded, loading insecure: [this=%p]",this));Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_RESULT,HSTSPrimingResult::eHSTS_PRIMING_SUCCEEDED_HTTP);// log HTTP_SCHEME_UPGRADE telemetryTelemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE,0);nsresultrv=ContinueConnect();if(NS_FAILED(rv)){CloseCacheEntry(false);returnAsyncAbort(rv);}returnNS_OK;}/* * May be invoked synchronously if HSTS priming has already been performed * for the host. */nsresultnsHttpChannel::OnHSTSPrimingFailed(nsresultaError,boolaCached){boolwouldBlock=mLoadInfo->GetMixedContentWouldBlock();// Clear out the HSTS priming flags on the LoadInfo to simplify the logic in// TryHSTSPriming()mLoadInfo->ClearHSTSPriming();LOG(("HSTS Priming Failed [this=%p], %s the load",this,(wouldBlock)?"blocking":"allowing"));if(aError==NS_ERROR_HSTS_PRIMING_TIMEOUT){// A priming request was sent, but timed outTelemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_RESULT,(wouldBlock)?HSTSPrimingResult::eHSTS_PRIMING_TIMEOUT_BLOCK:HSTSPrimingResult::eHSTS_PRIMING_TIMEOUT_ACCEPT);}elseif(aCached){// Between the time we marked for priming and started the priming request,// the host was found to not allow the upgrade, probably from another// priming request.Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_RESULT,(wouldBlock)?HSTSPrimingResult::eHSTS_PRIMING_CACHED_BLOCK:HSTSPrimingResult::eHSTS_PRIMING_CACHED_NO_UPGRADE);}else{// A priming request was sent, and no HSTS header was found that allows// the upgrade.Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_RESULT,(wouldBlock)?HSTSPrimingResult::eHSTS_PRIMING_FAILED_BLOCK:HSTSPrimingResult::eHSTS_PRIMING_FAILED_ACCEPT);}// Don't visit again for at least// security.mixed_content.hsts_priming_cache_timeout seconds.nsISiteSecurityService*sss=gHttpHandler->GetSSService();NS_ENSURE_TRUE(sss,NS_ERROR_OUT_OF_MEMORY);OriginAttributesoriginAttributes;NS_GetOriginAttributes(this,originAttributes);nsresultrv=sss->CacheNegativeHSTSResult(mURI,nsMixedContentBlocker::sHSTSPrimingCacheTimeout,originAttributes);if(NS_FAILED(rv)){NS_ERROR("nsISiteSecurityService::CacheNegativeHSTSResult failed");}// If we would block, go ahead and abort with the error providedif(wouldBlock){CloseCacheEntry(false);returnAsyncAbort(aError);}// log HTTP_SCHEME_UPGRADE telemetryTelemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE,0);// we can continue the load and the UI has been updated as mixed contentrv=ContinueConnect();if(NS_FAILED(rv)){CloseCacheEntry(false);returnAsyncAbort(rv);}returnNS_OK;}//-----------------------------------------------------------------------------// AChannelHasDivertableParentChannelAsListener internal functions//-----------------------------------------------------------------------------NS_IMETHODIMPnsHttpChannel::MessageDiversionStarted(ADivertableParentChannel*aParentChannel){LOG(("nsHttpChannel::MessageDiversionStarted [this=%p]",this));MOZ_ASSERT(!mParentChannel);mParentChannel=aParentChannel;// If the channel is suspended, propagate that info to the parent's mEventQ.uint32_tsuspendCount=mSuspendCount;while(suspendCount--){mParentChannel->SuspendMessageDiversion();}returnNS_OK;}NS_IMETHODIMPnsHttpChannel::MessageDiversionStop(){LOG(("nsHttpChannel::MessageDiversionStop [this=%p]",this));MOZ_ASSERT(mParentChannel);mParentChannel=nullptr;returnNS_OK;}NS_IMETHODIMPnsHttpChannel::SuspendInternal(){NS_ENSURE_TRUE(mIsPending,NS_ERROR_NOT_AVAILABLE);LOG(("nsHttpChannel::SuspendInternal [this=%p]\n",this));++mSuspendCount;if(mSuspendCount==1){mSuspendTimestamp=TimeStamp::NowLoRes();}nsresultrvTransaction=NS_OK;if(mTransactionPump){rvTransaction=mTransactionPump->Suspend();}nsresultrvCache=NS_OK;if(mCachePump){rvCache=mCachePump->Suspend();}returnNS_FAILED(rvTransaction)?rvTransaction:rvCache;}NS_IMETHODIMPnsHttpChannel::ResumeInternal(){NS_ENSURE_TRUE(mSuspendCount>0,NS_ERROR_UNEXPECTED);LOG(("nsHttpChannel::ResumeInternal [this=%p]\n",this));if(--mSuspendCount==0){mSuspendTotalTime+=(TimeStamp::NowLoRes()-mSuspendTimestamp).ToMilliseconds();if(mCallOnResume){nsresultrv=AsyncCall(mCallOnResume);mCallOnResume=nullptr;NS_ENSURE_SUCCESS(rv,rv);}}nsresultrvTransaction=NS_OK;if(mTransactionPump){rvTransaction=mTransactionPump->Resume();}nsresultrvCache=NS_OK;if(mCachePump){rvCache=mCachePump->Resume();}returnNS_FAILED(rvTransaction)?rvTransaction:rvCache;}voidnsHttpChannel::MaybeWarnAboutAppCache(){// First, accumulate a telemetry ping about appcache usage.Telemetry::Accumulate(Telemetry::HTTP_OFFLINE_CACHE_DOCUMENT_LOAD,true);// Then, issue a deprecation warning.nsCOMPtr<nsIDeprecationWarner>warner;GetCallback(warner);if(warner){warner->IssueWarning(nsIDocument::eAppCache,false);}}voidnsHttpChannel::SetLoadGroupUserAgentOverride(){nsCOMPtr<nsIURI>uri;GetURI(getter_AddRefs(uri));nsAutoCStringuriScheme;if(uri){uri->GetScheme(uriScheme);}// We don't need a UA for file: protocols.if(uriScheme.EqualsLiteral("file")){gHttpHandler->OnUserAgentRequest(this);return;}nsIRequestContextService*rcsvc=gHttpHandler->GetRequestContextService();nsCOMPtr<nsIRequestContext>rc;if(rcsvc){rcsvc->GetRequestContext(mRequestContextID,getter_AddRefs(rc));}nsAutoCStringua;if(nsContentUtils::IsNonSubresourceRequest(this)){gHttpHandler->OnUserAgentRequest(this);if(rc){GetRequestHeader(NS_LITERAL_CSTRING("User-Agent"),ua);rc->SetUserAgentOverride(ua);}}else{GetRequestHeader(NS_LITERAL_CSTRING("User-Agent"),ua);// Don't overwrite the UA if it is already set (eg by an XHR with explicit UA).if(ua.IsEmpty()){if(rc){rc->GetUserAgentOverride(ua);SetRequestHeader(NS_LITERAL_CSTRING("User-Agent"),ua,false);}else{gHttpHandler->OnUserAgentRequest(this);}}}}voidnsHttpChannel::SetDoNotTrack(){/** * 'DoNotTrack' header should be added if 'privacy.donottrackheader.enabled' * is true or tracking protection is enabled. See bug 1258033. */nsCOMPtr<nsILoadContext>loadContext;NS_QueryNotificationCallbacks(this,loadContext);if((loadContext&&loadContext->UseTrackingProtection())||nsContentUtils::DoNotTrackEnabled()){DebugOnly<nsresult>rv=mRequestHead.SetHeader(nsHttp::DoNotTrack,NS_LITERAL_CSTRING("1"),false);MOZ_ASSERT(NS_SUCCEEDED(rv));}}voidnsHttpChannel::ReportRcwnStats(nsIRequest*firstResponseRequest,boolisFromNet){if(!sRCWNEnabled){return;}enumRaceCacheAndNetStatus{kDidNotRaceUsedNetwork=0,kDidNotRaceUsedCache=1,kRaceUsedNetwork=2,kRaceUsedCache=3};staticconstTelemetry::HistogramIDkRcwnTelemetry[4]={Telemetry::NETWORK_RACE_CACHE_BANDWIDTH_NOT_RACE,Telemetry::NETWORK_RACE_CACHE_BANDWIDTH_NOT_RACE,Telemetry::NETWORK_RACE_CACHE_BANDWIDTH_RACE_NETWORK_WIN,Telemetry::NETWORK_RACE_CACHE_BANDWIDTH_RACE_CACHE_WIN};RaceCacheAndNetStatusrcwnStatus=kDidNotRaceUsedNetwork;if(isFromNet){rcwnStatus=mRaceCacheWithNetwork?kRaceUsedNetwork:kDidNotRaceUsedNetwork;}elseif(firstResponseRequest==mCachePump){rcwnStatus=mRaceCacheWithNetwork?kRaceUsedCache:kDidNotRaceUsedCache;}Telemetry::Accumulate(Telemetry::NETWORK_RACE_CACHE_WITH_NETWORK_USAGE,rcwnStatus);Telemetry::Accumulate(kRcwnTelemetry[rcwnStatus],mTransferSize);gIOService->IncrementRequestNumber();if(rcwnStatus==kRaceUsedCache){gIOService->IncrementCacheWonRequestNumber();}elseif(rcwnStatus==kRaceUsedNetwork){gIOService->IncrementNetWonRequestNumber();}}staticconstsize_tkPositiveBucketNumbers=34;staticconstint64_tkPositiveBucketLevels[kPositiveBucketNumbers]={0,10,20,30,40,50,60,70,80,90,100,200,300,400,500,600,700,800,900,1000,2000,3000,4000,5000,6000,7000,8000,9000,10000,20000,30000,40000,50000,60000};/** * For space efficiency, we collect finer resolution for small difference * between net and cache time, coarser for larger. * Bucket #40 for a tie. * #41 to #50 indicates cache wins by 1ms to 100ms, split equally. * #51 to #59 indicates cache wins by 101ms to 1000ms. * #60 to #68 indicates cache wins by 1s to 10s. * #69 to #73 indicates cache wins by 11s to 60s. * #74 indicates cache wins by more than 1 minute. * * #39 to #30 indicates network wins by 1ms to 100ms, split equally. * #29 to #21 indicates network wins by 101ms to 1000ms. * #20 to #12 indicates network wins by 1s to 10s. * #11 to #7 indicates network wins by 11s to 60s. * #6 indicates network wins by more than 1 minute. * * Other bucket numbers are reserved. */inlineint64_tnsHttpChannel::ComputeTelemetryBucketNumber(int64_tdifftime_ms){int64_tabsBucketIndex=std::lower_bound(kPositiveBucketLevels,kPositiveBucketLevels+kPositiveBucketNumbers,static_cast<int64_t>(mozilla::Abs(difftime_ms)))-kPositiveBucketLevels;returndifftime_ms>=0?40+absBucketIndex:40-absBucketIndex;}voidnsHttpChannel::ReportNetVSCacheTelemetry(){nsresultrv;if(!mCacheEntry){return;}// We only report telemetry if the entry is persistent (on disk)boolpersistent;rv=mCacheEntry->GetPersistent(&persistent);if(NS_FAILED(rv)||!persistent){return;}uint64_tonStartNetTime=0;if(NS_FAILED(mCacheEntry->GetOnStartTime(&onStartNetTime))){return;}uint64_tonStopNetTime=0;if(NS_FAILED(mCacheEntry->GetOnStopTime(&onStopNetTime))){return;}uint64_tonStartCacheTime=(mOnStartRequestTimestamp-mAsyncOpenTime).ToMilliseconds();int64_tonStartDiff=onStartNetTime-onStartCacheTime;onStartDiff=ComputeTelemetryBucketNumber(onStartDiff);uint64_tonStopCacheTime=(mCacheReadEnd-mAsyncOpenTime).ToMilliseconds();int64_tonStopDiff=onStopNetTime-onStopCacheTime;onStopDiff=ComputeTelemetryBucketNumber(onStopDiff);if(mDidReval){Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTART_REVALIDATED_V2,onStartDiff);Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTOP_REVALIDATED_V2,onStopDiff);}else{Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTART_NOTREVALIDATED_V2,onStartDiff);Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTOP_NOTREVALIDATED_V2,onStopDiff);}if(mDidReval){// We don't report revalidated probes as the data would be skewed.return;}if(mCacheOpenWithPriority){if(mCacheQueueSizeWhenOpen<5){Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTART_QSMALL_HIGHPRI_V2,onStartDiff);Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTOP_QSMALL_HIGHPRI_V2,onStopDiff);}elseif(mCacheQueueSizeWhenOpen<10){Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTART_QMED_HIGHPRI_V2,onStartDiff);Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTOP_QMED_HIGHPRI_V2,onStopDiff);}else{Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTART_QBIG_HIGHPRI_V2,onStartDiff);Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTOP_QBIG_HIGHPRI_V2,onStopDiff);}}else{// The limits are higher for normal priority cache queuesif(mCacheQueueSizeWhenOpen<10){Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTART_QSMALL_NORMALPRI_V2,onStartDiff);Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTOP_QSMALL_NORMALPRI_V2,onStopDiff);}elseif(mCacheQueueSizeWhenOpen<50){Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTART_QMED_NORMALPRI_V2,onStartDiff);Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTOP_QMED_NORMALPRI_V2,onStopDiff);}else{Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTART_QBIG_NORMALPRI_V2,onStartDiff);Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTOP_QBIG_NORMALPRI_V2,onStopDiff);}}uint32_tdiskStorageSizeK=0;rv=mCacheEntry->GetDiskStorageSizeInKB(&diskStorageSizeK);if(NS_FAILED(rv)){return;}// No significant difference was observed between different sizes for |onStartDiff|if(diskStorageSizeK<256){Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTOP_SMALL_V2,onStopDiff);}else{Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTOP_LARGE_V2,onStopDiff);}}NS_IMETHODIMPnsHttpChannel::Test_delayCacheEntryOpeningBy(int32_taTimeout){MOZ_ASSERT(NS_IsMainThread(),"Must be called on the main thread");mCacheOpenDelay=aTimeout;returnNS_OK;}NS_IMETHODIMPnsHttpChannel::Test_triggerDelayedOpenCacheEntry(){MOZ_ASSERT(NS_IsMainThread(),"Must be called on the main thread");nsresultrv;if(!mCacheOpenDelay){// No delay was set.returnNS_ERROR_NOT_AVAILABLE;}if(!mCacheOpenFunc){// There should be a runnable.returnNS_ERROR_FAILURE;}if(mCacheOpenTimer){rv=mCacheOpenTimer->Cancel();if(NS_FAILED(rv)){returnrv;}mCacheOpenTimer=nullptr;}mCacheOpenDelay=0;// Avoid re-entrancy issues by nulling our mCacheOpenFunc before calling it.std::function<void(nsHttpChannel*)>cacheOpenFunc=nullptr;std::swap(cacheOpenFunc,mCacheOpenFunc);cacheOpenFunc(this);returnNS_OK;}nsresultnsHttpChannel::TriggerNetwork(int32_taTimeout){MOZ_ASSERT(NS_IsMainThread(),"Must be called on the main thread");// If a network request has already gone out, there is no point in// doing this again.LOG(("nsHttpChannel::TriggerNetwork [this=%p]\n",this));if(mNetworkTriggered){LOG((" network already triggered. Returning.\n"));returnNS_OK;}if(!aTimeout){mNetworkTriggered=true;if(mNetworkTriggerTimer){mNetworkTriggerTimer->Cancel();mNetworkTriggerTimer=nullptr;}// If we are waiting for a proxy request, that means we can't trigger// the next step just yet. We need for mConnectionInfo to be non-null// before we call TryHSTSPriming. OnProxyAvailable will trigger// BeginConnect, and Connect will call TryHSTSPriming even if it's// for the cache callbacks.if(mProxyRequest){LOG((" proxy request in progress. Delaying network trigger.\n"));mWaitingForProxy=true;returnNS_OK;}if(mCacheAsyncOpenCalled&&!mOnCacheAvailableCalled){mRaceCacheWithNetwork=true;}LOG((" triggering network\n"));returnTryHSTSPriming();}LOG((" setting timer to trigger network: %d ms\n",aTimeout));mNetworkTriggerTimer=do_CreateInstance(NS_TIMER_CONTRACTID);mNetworkTriggerTimer->InitWithCallback(this,aTimeout,nsITimer::TYPE_ONE_SHOT);returnNS_OK;}nsresultnsHttpChannel::MaybeRaceCacheWithNetwork(){// Don't trigger the network if the load flags say so.if(mLoadFlags&(LOAD_ONLY_FROM_CACHE|LOAD_NO_NETWORK_IO)){returnNS_OK;}// We must not race if the channel has a failure status code.if(NS_FAILED(mStatus)){returnNS_OK;}// If a CORS Preflight is required we must not race.if(mRequireCORSPreflight&&!mIsCorsPreflightDone){returnNS_OK;}uint32_tdelay;if(CacheFileUtils::CachePerfStats::IsCacheSlow()){// If the cache is slow, trigger the network request immediately.delay=0;}else{// Give cache a headstart of 3 times the average cache entry open time.delay=CacheFileUtils::CachePerfStats::GetAverage(CacheFileUtils::CachePerfStats::ENTRY_OPEN,true)*3;// We use microseconds in CachePerfStats but we need milliseconds// for TriggerNetwork.delay/=1000;}MOZ_ASSERT(sRCWNEnabled,"The pref must be truned on.");LOG(("nsHttpChannel::MaybeRaceCacheWithNetwork [this=%p, delay=%u]\n",this,delay));returnTriggerNetwork(delay);}NS_IMETHODIMPnsHttpChannel::Test_triggerNetwork(int32_taTimeout){MOZ_ASSERT(NS_IsMainThread(),"Must be called on the main thread");returnTriggerNetwork(aTimeout);}NS_IMETHODIMPnsHttpChannel::Notify(nsITimer*aTimer){RefPtr<nsHttpChannel>self(this);if(aTimer==mCacheOpenTimer){returnTest_triggerDelayedOpenCacheEntry();}elseif(aTimer==mNetworkTriggerTimer){returnTriggerNetwork(0);}else{MOZ_CRASH("Unknown timer");}returnNS_OK;}}// namespace net}// namespace mozilla